Disclaimer: The purpose of the Open Case Studies project is to demonstrate the use of various data science methods, tools, and software in the context of messy, real-world data. A given case study does not cover all aspects of the research process, is not claiming to be the most appropriate way to analyze a given data set, and should not be used in the context of making policy decisions without external consultation from scientific experts.

Motivation

A variety of different sources contribute different types of pollutants to what we call air pollution. Some sources are natural while others are anthropogenic (human derived):

[source]

Major types of air pollutants

  1. Gaseous - Carbon Monoxide (CO), Ozone (O3), Nitrogen Oxides(NO, NO2), Sulpher Dioxide (SO2)
  2. Particulate - small liquids and solids suspended in the air (includes lead- can include certain types of dust)
  3. Dust - small solids (larger than particulates) that can be suspended in the air for some time but eventually settle
  4. Biological - pollen, bacteria, viruses, mold spores

See [here])http://www.redlogenv.com/worker-safety/part-1-dust-and-particulate-matter) for more detail on the types of pollutants in the air.

Particulate pollution

Air pollution particulates are generally described by their size.

There are 3 major categories:

  1. Large Coarse Particulate Mater - has diameter of >10 micrometers (10 µm)

  2. Coarse Particulate Mater (called PM10-2.5) - has diameter of between 2.5 µm and 10 µm

  3. Fine Particulate Mater (called PM2.5) - has diameter of < 2.5 µm

PM10 includes any particulate mater <10 µm (both coarse and fine particulate mater)

Here you can see how these sizes compare with a human hair:

[source]

The following plot and table show the relative sizes of these different pollutants in micrometers(µm):

[source]

[source]

This table shows how deeply some of the smaller fine particles can penetrate within the human body:

[source]

Negative Impact of Particulate Exposure on Health

Exposure to air pollution is associated with higher rates of mortality in older adults and is known to be a risk factor for many diseases and conditions including but not limited to:

  1. Asthma - fine particle exposure (PM2.5) was found to be associated with higher rates of asthma in children
  2. Inflammation in type 1 diabetes - fine particle exposure (PM2.5) from traffic-related air pollution was associated with increased measures of inflammatory markers in youths with type 1 diabetes
  3. Lung function and emphysema - higher concentrations of ozone (O3), nitrogen oxides (NOx), black carbon, and fine particle exposure PM2.5 , at study baseline were significantly associated with greater increases in percent emphysema per 10 years
  4. Low birthweight - fine particle exposure(PM2.5) was associated with lower birth weight in full-term live births
  5. Viral Infection - higher rates of infection and increased severity of infection are associated with higher exposures to pollution levels including fine particle exposure (PM2.5)

See this review article for more information about sources of air pollution and the influence of air pollution on health.

Sparse Monitoring is Problematic for Public Health

Historically epidemiological studies would assess the influence of air pollution on health outcomes by relying on a number of monitors located around the country. However as can be seen in the following figure, these monitors remain to be relatively sparse in certain regions of the country. Furthermore, dramatic differences in pollution rates can be seen even within the same city.

[source]

This lack of granularity in air pollution monitoring has hindered our ability to discern the full impact of air pollution on health and to identify at-risk locations.

Machine Learning Offers a Solution

An article published in the Environmental Health journal dealt with this issue by using data about population density, road density, among other features to model or predict air pollution levels at a more localized scale using machine learning methods.

Yanosky, J. D. et al. Spatio-temporal modeling of particulate air pollution in the conterminous United States using geographic and meteorological predictors. Environ Health 13, 63 (2014).

The authors of this article state that:

“Exposure to atmospheric particulate matter (PM) remains an important public health concern, although it remains difficult to quantify accurately across large geographic areas with sufficiently high spatial resolution. Recent epidemiologic analyses have demonstrated the importance of spatially- and temporally-resolved exposure estimates, which show larger PM-mediated health effects as compared to nearest monitor or county-specific ambient concentrations.”

The article above explains that machine learning methods can be used to predict air pollution levels when traditional monitoring systems are not available in a particular area or when there is not enough spatial granularity with current monitoring systems. We will use similar methods to predict annual air pollution levels spatially within the US.

Main Questions

Our main question:

  1. Can we predict annual average air pollution concentrations at the granularity of zip code regional levels using predictors such as data about population density, urbanization, road density, as well as, satellite pollution data and chemical modeling data?

Learning Objectives

In this case study, we will walk you through importing data from CSV files and performing machine learning methods to predict our outcome variable of interest (in this case annual fine particle air pollution estimates). We will especially focus on using packages and functions from the Tidyverse, and more specifically the tidymodels package/ecosystem primarily developed and maintained by Max Kuhn and Davis Vaughan. This package loads more modeling related packages like rsample, recipes, parsnip, yardstick, and dials. We will also briefly cover the workflows and tune packages. The tidyverse is a library of packages created by RStudio. While some students may be familiar with previous R programming packages, these packages make data science in R especially efficient.

We will begin by loading the packages that we will need:

Package Use
here to easily load and save data
readr to import the CSV file data
dplyr to view/arrange/filter/select/compare specific subsets of the data
skimr to get an overview of data
summarytools to get an overview of data in a different style
magrittr to use the %<>% pipping operator
corrplot to make large correlation plots
ggcorrplot also to make large correlation plots
GGally to make smaller correlation plots
rsample to split the data into testing and training sets and to split the training set for cross-validation
recipes to pre-process data for modeling in a tidy and reproducible way and to extract pre-processed data (major functions are recipe() , prep() and various transformation step_*() functions, as well as juice() - extracts final preprocessed training data and bake() - applies recipe steps to testing data). See here for more info.
parsnip an interface to create models (major functions are fit(), set_engine())
yardstick to evaluate the performance of models

stringr | to manipulate the text within the PDF of the data

purrr | to perform functions on all columns of a tibble tibble | to create data objects that we can manipulate with dplyr/stringr/tidyr/purrr tidyr | to separate data within a column into multiple columns ggplot2 | to make visualizations with multiple layers ggpubr | to easily add regression line equations to plots forcats | to change details about factors (categorical variables) lmerTest| to perform linear mixed model testing car| to perform Levene’s Test of Homogeneity of Variances ggiraph| to make plots interactive ggforce| to modify facets in plots viridis| to plot in color palette that is easily interpreted by colorblind individuals cowplot | to allow plots to be combined ___

The first time we use a function, we will use the :: to indicate which package we are using. Unless we have overlapping function names, this is not necessary, but we will include it here to be informative about where the functions we will use come from.

Context

The State of Global Air is a report released every year to communicate the impact of air pollution on public health.

The State of Global Air 2019 report which uses data from 2017 stated that:

Air pollution is the fifth leading risk factor for mortality worldwide. It is responsible for more deaths than many better-known risk factors such as malnutrition, alcohol use, and physical inactivity. Each year, more people die from air pollution–related disease than from road traffic injuries or malaria.

The report also stated that:

In 2017, air pollution is estimated to have contributed to close to 5 million deaths globally — nearly 1 in every 10 deaths.

##### [source]

The State of Global Air 2018 report using data from 2016 which separated different types of air pollution, found that particulate pollution was particularly associated with mortality.

The 2019 report shows that the highest levels of fine particulate pollution occurs in Africa and Asia and that:

More than 90% of people worldwide live in areas exceeding the World Health Organization (WHO) Guideline for healthy air. More than half live in areas that do not even meet WHO’s least-stringent air quality target.

Looking at the US specifically, air pollution levels are generally improving. The US Environmental Protection Agency (EPA) also releases a report about air pollution levels called Our Nation’s Air.

[source]

However, air pollution continues to contribute to health risk for Americans, in particular in regions with higher than national average rates of pollution that actually at time exceed the world health organization’s recommended level. Thus it is necessary to obtain high spatial granularity in estimates of air pollution in order to identify locations where populations are experiencing harmful levels of exposure.

You can see that current air quality conditions at this website and you will notice variation across different cities.

Here were the conditions in Topeka Kansas when this was written:

It reports particulate values using what is called the Air Quality Index scale (AQI), this calculator indicates that 114 AQI is equivalent to 40.7 ug/m3 and is considered unhealthy for sensitive individuals. Thus some areas very much exceed the World Health Organization (WHO) annual exposure guideline (10 ug/m3) at certain times and this may adversely affect the health of people living in these locations.

Furthermore, adverse health effects have been associated with populations experiencing higher pollution exposure despite the levels being below suggested guidelines. Secondly, it appears that the composition of the particulate mater and the influence of other demographic factors may make specific populations more at risk for adverse health effects due to air pollution. See this article for more details.

The monitor data that we will use in this case study comes from a system of monitors in which roughly 90% are located within cities. Thus there is an equity issue in terms of capturing the air pollution levels of more rural areas. Therefore, to get a better sense of the pollution exposures for the individuals living in these areas, methods like machine learning can be very useful to estimate air pollution levels in areas with little to no monitoring.

Indeed, machine learning methods are in fact used to be able to estimate air pollution in these low monitoring areas so that we can make a map like this where we have annual estimates for all of the contiguous US:

[source]

This is what we aim to achieve in this case study.

Limitations

There are some important considerations regarding this data analysis to keep in mind:

  1. The data in our analysis does not include information about the composition of particulate mater. Different types of particulates may be more benign or deleterious for health outcomes.

  2. Outdoor pollution levels are not necessarily an indication of of individual exposures. People spend differing amounts of time indoors and outdoors and are exposed to different pollution levels indoors. People are now developing personal monitoring systems to track air pollution levels on the personal level.

Our analysis will use annual mean estimates, however pollution levels can vary greatly by season, day and even hour. There are data sources that have finer levels of temporal data, however we are interested in long term exposures, as these appear to be the most influential for health outcomes, so we chose to use annual level data.

What are the data?

In Machine Learning for prediction, there are two main types of variables:

  1. Outcome variable
  2. Predictor variables

The outcome variable is what are trying to predict. In building our model we actually have the outcome variable data, but we want to see how well our predictor variables can explain the variation in our outcome data. This gives us a sense of how well we can use the predictor variable data to predict our outcome variable levels when we in fact do not have data about the outcome.

As a simpler example, imagine that we have data about the sales and characteristics of cars from last year and we want to predict which cars might sell well this year. We do not have the sales data yet for this year, but we do know the characteristics of our cars for this year. We can use a model of the characteristics that explained sales last year to estimate what cars might sell well this year. In this case, our outcome variable is the sale performance of the cars, while the different characteristics of the cars make up our predictor or explanatory variables.

In this case study, we will evaluate air pollution monitor data of fine particulate mater (PM2.5) in the contiguous US from 2008, as well as data about population density, road density, urbanization levels, and NASA satellite data to develop models to predict localized air pollution levels.

The monitor data will be our outcome variable. We want to determine if we can predict air pollution levels based on other types of data, like road density and population density to see if we can use these data to predict air pollution in areas where there are no monitors.

Our outcome variable

The monitor data that we will be using comes from gravimetric monitors operated by the US Enivornmental Protection Agency (EPA). These monitors use a filtration system to specifically capture fine particulate matter. The weight of this matter is manually measured daily or weekly. See here for the EPA standard operating procedure for PM gravimetric analysis in 2008.

[source]

Here is an image of what the gravimetric monitors look like:

Gravimetric analysis is also used for emission testing. The same idea applies: a fresh filter is applied and the desired amount of time passes, then the filter is removed and weighed.

There are other monitoring systems that can provide hourly measurements, but we will not be using data from these monitors in our analysis. Gravimetric analysis is considered to be among the most accurate methods.

In our csv, the value column indicates the PM2.5 monitor average for 2008 in mass of fine particles/volume of air for 876 gravimetric monitors. The units are micro grams of fine particulate mater (PM) that is less than 2.5 micrometers in diameter per cubic meter of air - mass concentration (ug/m3). Recall the the WHO exposure guideline is < 10 ug/m3 on average annually for PM2.5.

Our predictor variables

There are 48 predictor variables with values for each of the 876 monitors included in our outcome variable. The data comes from the US Enivornmental Protection Agency (EPA), the National Aeronautics and Space Administration (NASA), the US Census, and the National Center for Health Statistics (NCHS).

Variable Details
id Monitor number
– the county number is indicated before the decimal
– the monitor number is indicated after the decimal
Example: 1073.0023 is Jefferson county (1073) and .0023 one of 8 monitors
fips Federal information processing standard number for the county where the monitor is located
– 5 digit id code for counties (zero is often the first value and sometimes is not shown)
– the first 2 numbers indicate the state
– the last three numbers indicate the county
Example: Alabama’s state code is 01 because it is first alphabetically
(note: Alaska and Hawaii are not included because they are not part of the contiguous US)
Lat Latitude of the monitor in degrees
Lon Longitude of the monitor in degrees
state State where the monitor is located
county County where the monitor is located
city City where the monitor is located
CMAQ Estimated values of air pollution from a computational model called Community Multiscale Air Quality (CMAQ)
– A monitoring system that simulates the physics of the atmosphere using chemistry and weather data to predict the air pollution
Does not use any of the PM2.5 gravimetric monitoring data. (There is a version that does use the gravimetric monitoring data, but not this one!)
– Data from the EPA
zcta Zip Code Tabulation Area where the monitor is located
– Postal Zip codes are converted into “generalized areal representations” that are non-overlapping
– Data from the 2010 Census
zcta_area Land area of the zip code area in meters squared
– Data from the 2010 Census
zcta_pop Population in the zip code area
– Data from the 2010 Census
imp_a500 Impervious surface measure
– Within a circle with a radius of 500 meters around the monitor
– Impervious surface are roads, concrete, parking lots, buildings
– This is a measure of development
imp_a1000 Impervious surface measure
– Within a circle with a radius of 1000 meters around the monitor
imp_a5000 Impervious surface measure
– Within a circle with a radius of 5000 meters around the monitor
imp_a10000 Impervious surface measure
– Within a circle with a radius of 10000 meters around the monitor
imp_a15000 Impervious surface measure
– Within a circle with a radius of 15000 meters around the monitor
county_area Land area of the county of the monitor in meters squared
county_pop Population of the county of the monitor
Log_dist_to_prisec Log (Natural log) distance to a primary or secondary road from the monitor
– Highway or major road
log_pri_length_5000 Count of primary road length in meters in a circle with a radius of 5000 meters around the monitor (Natural log)
– Highways only
log_pri_length_10000 Count of primary road length in meters in a circle with a radius of 10000 meters around the monitor (Natural log)
– Highways only
log_pri_length_15000 Count of primary road length in meters in a circle with a radius of 15000 meters around the monitor (Natural log)
– Highways only
log_pri_length_25000 Count of primary road length in meters in a circle with a radius of 25000 meters around the monitor (Natural log)
– Highways only
log_prisec_length_500 Count of primary and secondary road length in meters in a circle with a radius of 500 meters around the monitor (Natural log)
– Highway and secondary roads
log_prisec_length_1000 Count of primary and secondary road length in meters in a circle with a radius of 1000 meters around the monitor (Natural log)
– Highway and secondary roads
log_prisec_length_5000 Count of primary and secondary road length in meters in a circle with a radius of 5000 meters around the monitor (Natural log)
– Highway and secondary roads
log_prisec_length_10000 Count of primary and secondary road length in meters in a circle with a radius of 10000 meters around the monitor (Natural log)
– Highway and secondary roads
log_prisec_length_15000 Count of primary and secondary road length in meters in a circle with a radius of 15000 meters around the monitor (Natural log)
– Highway and secondary roads
log_prisec_length_25000 Count of primary and secondary road length in meters in a circle with a radius of 25000 meters around the monitor (Natural log)
– Highway and secondary roads
log_nei_2008_pm25_sum_10000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 10000 meters of distance around the monitor (Natural log)
log_nei_2008_pm25_sum_15000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 15000 meters of distance around the monitor (Natural log)
log_nei_2008_pm25_sum_25000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 25000 meters of distance around the monitor (Natural log)
log_nei_2008_pm10_sum_10000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 10000 meters of distance around the monitor (Natural log)
log_nei_2008_pm10_sum_15000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 15000 meters of distance around the monitor (Natural log)
log_nei_2008_pm10_sum_25000 Tons of emissions from major sources data base (annual data) sum of all sources within a circle with a radius of 25000 meters of distance around the monitor (Natural log)
popdens_county Population density (number of people per kilometer squared area of the county)
popdens_zcta Population density (number of people per kilometer squared area of zcta)
nohs Percentage of people in zcta area where the monitor is that do not have a high school degree
– Data from the Census
somehs Percentage of people in zcta area where the monitor whose highest formal educational attainment was some high school education
– Data from the Census
hs Percentage of people in zcta area where the monitor whose highest formal educational attainment was completing a high school degree
– Data from the Census
somecollege Percentage of people in zcta area where the monitor whose highest formal educational attainment was completing some college education
– Data from the Census
associate Percentage of people in zcta area where the monitor whose highest formal educational attainment was completing an associate degree
– Data from the Census
bachelor Percentage of people in zcta area where the monitor whose highest formal educational attainment was a bachelor’s degree
– Data from the Census
grad Percentage of people in zcta area where the monitor whose highest formal educational attainment was a graduate degree
– Data from the Census
pov Percentage of people in zcta area where the monitor is that lived in poverty in 2008 - or would it have been 2007 guidelines??https://aspe.hhs.gov/2007-hhs-poverty-guidelines
– Data from the Census
hs_orless Percentage of people in zcta area where the monitor whose highest formal educational attainment was a high school degree or less (sum of nohs, somehs, and hs)
urc2013 2013 Urban-rural classification of the county where the monitor is located
– 6 category variable - 1 is totally urban 6 is completely rural
– Data from the National Center for Health Statistics](https://www.cdc.gov/nchs/index.htm){target="_blank"}
urc2006 2006 Urban-rural classification of the county where the monitor is located
– 6 category variable - 1 is totally urban 6 is completely rural
– Data from the National Center for Health Statistics
aod Aerosol Optical Depth measurement from a NASA satellite
– based on the defraction of a laser
– used as a proxy of particulate pollution
– unit-less - higher value indicates more pollution
– Data from NASA

Many of these predictor variables have to do with the circular area around the monitor called the “buffer”. These are illustrated in the following figure:

[source]

Data Import

We have one CSV file that contains both our single outcome variable and all of our predictor variables.

Let’s import our data into R now so that we can explore the data further. We will call our data object pm for particulate matter.

Data Exploration and Wrangling

The first step in performing a machine learning analysis is to explore the data to better understand the variables included in the data, as we may learn about important details about the data that we should keep in mind as we try to predict our outcome variable.

First let’s just get a general sense of our data. We can do that using the glimpse() function of the dplyr package (it is also in the tibble package).

We will also use the %>% pipe which can be used to define the input for later sequential steps. This will make more sense when we have multiple sequential steps using the same data object. To use the pipe notation we need to install and load dplyr as well.

For example here we will first grab the pm data object, then we use the glimpse() function on it based on the pipe notation.

Rows: 876
Columns: 50
$ id                          <dbl> 1003.001, 1027.000, 1033.100, 1049.100, 1…
$ value                       <dbl> 9.597647, 10.800000, 11.212174, 11.659091…
$ fips                        <dbl> 1003, 1027, 1033, 1049, 1055, 1069, 1073,…
$ lat                         <dbl> 30.49800, 33.28126, 34.75878, 34.28763, 3…
$ lon                         <dbl> -87.88141, -85.80218, -87.65056, -85.9683…
$ state                       <chr> "Alabama", "Alabama", "Alabama", "Alabama…
$ county                      <chr> "Baldwin", "Clay", "Colbert", "DeKalb", "…
$ city                        <chr> "Fairhope", "Ashland", "Muscle Shoals", "…
$ CMAQ                        <dbl> 8.098836, 9.766208, 9.402679, 8.534772, 9…
$ zcta                        <dbl> 36532, 36251, 35660, 35962, 35901, 36303,…
$ zcta_area                   <dbl> 190980522, 374132430, 16716984, 203836235…
$ zcta_pop                    <dbl> 27829, 5103, 9042, 8300, 20045, 30217, 90…
$ imp_a500                    <dbl> 0.01730104, 1.96972318, 19.17301038, 5.78…
$ imp_a1000                   <dbl> 1.4096021, 0.8531574, 11.1448962, 3.86764…
$ imp_a5000                   <dbl> 3.3360118, 0.9851479, 15.1786154, 1.23114…
$ imp_a10000                  <dbl> 1.9879187, 0.5208189, 9.7253870, 1.031646…
$ imp_a15000                  <dbl> 1.4386207, 0.3359198, 5.2472094, 0.973044…
$ county_area                 <dbl> 4117521611, 1564252280, 1534877333, 20126…
$ county_pop                  <dbl> 182265, 13932, 54428, 71109, 104430, 1015…
$ log_dist_to_prisec          <dbl> 4.648181, 7.219907, 5.760131, 3.721489, 5…
$ log_pri_length_5000         <dbl> 8.517193, 8.517193, 8.517193, 8.517193, 9…
$ log_pri_length_10000        <dbl> 9.210340, 9.210340, 9.274303, 10.409411, …
$ log_pri_length_15000        <dbl> 9.630228, 9.615805, 9.658899, 11.173626, …
$ log_pri_length_25000        <dbl> 11.32735, 10.12663, 10.15769, 11.90959, 1…
$ log_prisec_length_500       <dbl> 7.295356, 6.214608, 8.611945, 7.310155, 8…
$ log_prisec_length_1000      <dbl> 8.195119, 7.600902, 9.735569, 8.585843, 9…
$ log_prisec_length_5000      <dbl> 10.815042, 10.170878, 11.770407, 10.21420…
$ log_prisec_length_10000     <dbl> 11.88680, 11.40554, 12.84066, 11.50894, 1…
$ log_prisec_length_15000     <dbl> 12.205723, 12.042963, 13.282656, 12.35366…
$ log_prisec_length_25000     <dbl> 13.41395, 12.79980, 13.79973, 13.55979, 1…
$ log_nei_2008_pm25_sum_10000 <dbl> 0.318035438, 3.218632928, 6.573127301, 0.…
$ log_nei_2008_pm25_sum_15000 <dbl> 1.967358961, 3.218632928, 6.581917457, 3.…
$ log_nei_2008_pm25_sum_25000 <dbl> 5.067308, 3.218633, 6.875900, 4.887665, 4…
$ log_nei_2008_pm10_sum_10000 <dbl> 1.35588511, 3.31111648, 6.69187313, 0.000…
$ log_nei_2008_pm10_sum_15000 <dbl> 2.26783411, 3.31111648, 6.70127741, 3.350…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.628728, 3.311116, 7.148858, 5.171920, 4…
$ popdens_county              <dbl> 44.265706, 8.906492, 35.460814, 35.330814…
$ popdens_zcta                <dbl> 145.716431, 13.639555, 540.887040, 40.718…
$ nohs                        <dbl> 3.3, 11.6, 7.3, 14.3, 4.3, 5.8, 7.1, 2.7,…
$ somehs                      <dbl> 4.9, 19.1, 15.8, 16.7, 13.3, 11.6, 17.1, …
$ hs                          <dbl> 25.1, 33.9, 30.6, 35.0, 27.8, 29.8, 37.2,…
$ somecollege                 <dbl> 19.7, 18.8, 20.9, 14.9, 29.2, 21.4, 23.5,…
$ associate                   <dbl> 8.2, 8.0, 7.6, 5.5, 10.1, 7.9, 7.3, 8.0, …
$ bachelor                    <dbl> 25.3, 5.5, 12.7, 7.9, 10.0, 13.7, 5.9, 17…
$ grad                        <dbl> 13.5, 3.1, 5.1, 5.8, 5.4, 9.8, 2.0, 8.7, …
$ pov                         <dbl> 6.1, 19.5, 19.0, 13.8, 8.8, 15.6, 25.5, 7…
$ hs_orless                   <dbl> 33.3, 64.6, 53.7, 66.0, 45.4, 47.2, 61.4,…
$ urc2013                     <dbl> 4, 6, 4, 6, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ urc2006                     <dbl> 5, 6, 4, 5, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ aod                         <dbl> 37.36364, 34.81818, 36.00000, 33.08333, 4…

We can see that there are 876 monitors and that we have 50 total variables - one of which is the outcome. In this case our outcome variable is called value.

Notice that some of the variables that we would think of as factors (categorical) are currently of class double as indicated by the <dbl> just to the right of the column names/variable names in the glimpse() output. For example the monitor ID (id), the Federal Information Processing Standard number for the county where the monitor was located (fips), as well as the zcta

Let’s convert these variables into factors. We can do this using the mutate_at() function of the dplyr package and the as.factor() base function.

In this case we are also using the magrittr assignment pipe or double pipe that looks like this %<>% of the magrittr package. This allows us use the pm data as input but also reassign the output to the same data object name.

Rows: 876
Columns: 50
$ id                          <fct> 1003.001, 1027.0001, 1033.1002, 1049.1003…
$ value                       <dbl> 9.597647, 10.800000, 11.212174, 11.659091…
$ fips                        <fct> 1003, 1027, 1033, 1049, 1055, 1069, 1073,…
$ lat                         <dbl> 30.49800, 33.28126, 34.75878, 34.28763, 3…
$ lon                         <dbl> -87.88141, -85.80218, -87.65056, -85.9683…
$ state                       <chr> "Alabama", "Alabama", "Alabama", "Alabama…
$ county                      <chr> "Baldwin", "Clay", "Colbert", "DeKalb", "…
$ city                        <chr> "Fairhope", "Ashland", "Muscle Shoals", "…
$ CMAQ                        <dbl> 8.098836, 9.766208, 9.402679, 8.534772, 9…
$ zcta                        <fct> 36532, 36251, 35660, 35962, 35901, 36303,…
$ zcta_area                   <dbl> 190980522, 374132430, 16716984, 203836235…
$ zcta_pop                    <dbl> 27829, 5103, 9042, 8300, 20045, 30217, 90…
$ imp_a500                    <dbl> 0.01730104, 1.96972318, 19.17301038, 5.78…
$ imp_a1000                   <dbl> 1.4096021, 0.8531574, 11.1448962, 3.86764…
$ imp_a5000                   <dbl> 3.3360118, 0.9851479, 15.1786154, 1.23114…
$ imp_a10000                  <dbl> 1.9879187, 0.5208189, 9.7253870, 1.031646…
$ imp_a15000                  <dbl> 1.4386207, 0.3359198, 5.2472094, 0.973044…
$ county_area                 <dbl> 4117521611, 1564252280, 1534877333, 20126…
$ county_pop                  <dbl> 182265, 13932, 54428, 71109, 104430, 1015…
$ log_dist_to_prisec          <dbl> 4.648181, 7.219907, 5.760131, 3.721489, 5…
$ log_pri_length_5000         <dbl> 8.517193, 8.517193, 8.517193, 8.517193, 9…
$ log_pri_length_10000        <dbl> 9.210340, 9.210340, 9.274303, 10.409411, …
$ log_pri_length_15000        <dbl> 9.630228, 9.615805, 9.658899, 11.173626, …
$ log_pri_length_25000        <dbl> 11.32735, 10.12663, 10.15769, 11.90959, 1…
$ log_prisec_length_500       <dbl> 7.295356, 6.214608, 8.611945, 7.310155, 8…
$ log_prisec_length_1000      <dbl> 8.195119, 7.600902, 9.735569, 8.585843, 9…
$ log_prisec_length_5000      <dbl> 10.815042, 10.170878, 11.770407, 10.21420…
$ log_prisec_length_10000     <dbl> 11.88680, 11.40554, 12.84066, 11.50894, 1…
$ log_prisec_length_15000     <dbl> 12.205723, 12.042963, 13.282656, 12.35366…
$ log_prisec_length_25000     <dbl> 13.41395, 12.79980, 13.79973, 13.55979, 1…
$ log_nei_2008_pm25_sum_10000 <dbl> 0.318035438, 3.218632928, 6.573127301, 0.…
$ log_nei_2008_pm25_sum_15000 <dbl> 1.967358961, 3.218632928, 6.581917457, 3.…
$ log_nei_2008_pm25_sum_25000 <dbl> 5.067308, 3.218633, 6.875900, 4.887665, 4…
$ log_nei_2008_pm10_sum_10000 <dbl> 1.35588511, 3.31111648, 6.69187313, 0.000…
$ log_nei_2008_pm10_sum_15000 <dbl> 2.26783411, 3.31111648, 6.70127741, 3.350…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.628728, 3.311116, 7.148858, 5.171920, 4…
$ popdens_county              <dbl> 44.265706, 8.906492, 35.460814, 35.330814…
$ popdens_zcta                <dbl> 145.716431, 13.639555, 540.887040, 40.718…
$ nohs                        <dbl> 3.3, 11.6, 7.3, 14.3, 4.3, 5.8, 7.1, 2.7,…
$ somehs                      <dbl> 4.9, 19.1, 15.8, 16.7, 13.3, 11.6, 17.1, …
$ hs                          <dbl> 25.1, 33.9, 30.6, 35.0, 27.8, 29.8, 37.2,…
$ somecollege                 <dbl> 19.7, 18.8, 20.9, 14.9, 29.2, 21.4, 23.5,…
$ associate                   <dbl> 8.2, 8.0, 7.6, 5.5, 10.1, 7.9, 7.3, 8.0, …
$ bachelor                    <dbl> 25.3, 5.5, 12.7, 7.9, 10.0, 13.7, 5.9, 17…
$ grad                        <dbl> 13.5, 3.1, 5.1, 5.8, 5.4, 9.8, 2.0, 8.7, …
$ pov                         <dbl> 6.1, 19.5, 19.0, 13.8, 8.8, 15.6, 25.5, 7…
$ hs_orless                   <dbl> 33.3, 64.6, 53.7, 66.0, 45.4, 47.2, 61.4,…
$ urc2013                     <dbl> 4, 6, 4, 6, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ urc2006                     <dbl> 5, 6, 4, 5, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ aod                         <dbl> 37.36364, 34.81818, 36.00000, 33.08333, 4…

Great! Now we can see that these variables are now factors as indicated by <fct> after the variable name.

Packages to get a sense of the data

The skim() function of the skimr package is also really helpful for getting a general sense of your data.

Data summary
Name pm
Number of rows 876
Number of columns 50
_______________________
Column type frequency:
character 3
factor 3
numeric 44
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
state 0 1 4 20 0 49 0
county 0 1 3 20 0 471 0
city 0 1 4 48 0 607 0

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
id 0 1 FALSE 876 100: 1, 102: 1, 103: 1, 104: 1
fips 0 1 FALSE 569 170: 12, 603: 10, 261: 9, 107: 8
zcta 0 1 FALSE 842 475: 3, 110: 2, 160: 2, 290: 2

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
value 0 1 10.81 2.580000e+00 3.02 9.27 11.15 12.37 2.316000e+01 ▂▆▇▁▁
lat 0 1 38.48 4.620000e+00 25.47 35.03 39.30 41.66 4.840000e+01 ▁▃▅▇▂
lon 0 1 -91.74 1.496000e+01 -124.18 -99.16 -87.47 -80.69 -6.804000e+01 ▃▂▃▇▃
CMAQ 0 1 8.41 2.970000e+00 1.63 6.53 8.62 10.24 2.313000e+01 ▃▇▃▁▁
zcta_area 0 1 183173481.91 5.425989e+08 15459.00 14204601.75 37653560.50 160041508.25 8.164821e+09 ▇▁▁▁▁
zcta_pop 0 1 24227.58 1.777216e+04 0.00 9797.00 22014.00 35004.75 9.539700e+04 ▇▇▃▁▁
imp_a500 0 1 24.72 1.934000e+01 0.00 3.70 25.12 40.22 6.961000e+01 ▇▅▆▃▂
imp_a1000 0 1 24.26 1.802000e+01 0.00 5.32 24.53 38.59 6.750000e+01 ▇▅▆▃▁
imp_a5000 0 1 19.93 1.472000e+01 0.05 6.79 19.07 30.11 7.460000e+01 ▇▆▃▁▁
imp_a10000 0 1 15.82 1.381000e+01 0.09 4.54 12.36 24.17 7.209000e+01 ▇▃▂▁▁
imp_a15000 0 1 13.43 1.312000e+01 0.11 3.24 9.67 20.55 7.110000e+01 ▇▃▁▁▁
county_area 0 1 3768701992.12 6.212830e+09 33703512.00 1116536297.50 1690826566.50 2878192209.00 5.194723e+10 ▇▁▁▁▁
county_pop 0 1 687298.44 1.293489e+06 783.00 100948.00 280730.50 743159.00 9.818605e+06 ▇▁▁▁▁
log_dist_to_prisec 0 1 6.19 1.410000e+00 -1.46 5.43 6.36 7.15 1.045000e+01 ▁▁▃▇▁
log_pri_length_5000 0 1 9.82 1.080000e+00 8.52 8.52 10.05 10.73 1.205000e+01 ▇▂▆▅▂
log_pri_length_10000 0 1 10.92 1.130000e+00 9.21 9.80 11.17 11.83 1.302000e+01 ▇▂▇▇▃
log_pri_length_15000 0 1 11.50 1.150000e+00 9.62 10.87 11.72 12.40 1.359000e+01 ▆▂▇▇▃
log_pri_length_25000 0 1 12.24 1.100000e+00 10.13 11.69 12.46 13.05 1.436000e+01 ▅▃▇▇▃
log_prisec_length_500 0 1 6.99 9.500000e-01 6.21 6.21 6.21 7.82 9.400000e+00 ▇▁▂▂▁
log_prisec_length_1000 0 1 8.56 7.900000e-01 7.60 7.60 8.66 9.20 1.047000e+01 ▇▅▆▃▁
log_prisec_length_5000 0 1 11.28 7.800000e-01 8.52 10.91 11.42 11.83 1.278000e+01 ▁▁▃▇▃
log_prisec_length_10000 0 1 12.41 7.300000e-01 9.21 11.99 12.53 12.94 1.385000e+01 ▁▁▃▇▅
log_prisec_length_15000 0 1 13.03 7.200000e-01 9.62 12.59 13.13 13.57 1.441000e+01 ▁▁▃▇▅
log_prisec_length_25000 0 1 13.82 7.000000e-01 10.13 13.38 13.92 14.35 1.523000e+01 ▁▁▃▇▆
log_nei_2008_pm25_sum_10000 0 1 3.97 2.350000e+00 0.00 2.15 4.29 5.69 9.120000e+00 ▆▅▇▆▂
log_nei_2008_pm25_sum_15000 0 1 4.72 2.250000e+00 0.00 3.47 5.00 6.35 9.420000e+00 ▃▃▇▇▂
log_nei_2008_pm25_sum_25000 0 1 5.67 2.110000e+00 0.00 4.66 5.91 7.28 9.650000e+00 ▂▂▇▇▃
log_nei_2008_pm10_sum_10000 0 1 4.35 2.320000e+00 0.00 2.69 4.62 6.07 9.340000e+00 ▅▅▇▇▂
log_nei_2008_pm10_sum_15000 0 1 5.10 2.180000e+00 0.00 3.87 5.39 6.72 9.710000e+00 ▂▃▇▇▂
log_nei_2008_pm10_sum_25000 0 1 6.07 2.010000e+00 0.00 5.10 6.37 7.52 9.880000e+00 ▁▂▆▇▃
popdens_county 0 1 551.76 1.711510e+03 0.26 40.77 156.67 510.81 2.682191e+04 ▇▁▁▁▁
popdens_zcta 0 1 1279.66 2.757490e+03 0.00 101.15 610.35 1382.52 3.041884e+04 ▇▁▁▁▁
nohs 0 1 6.99 7.210000e+00 0.00 2.70 5.10 8.80 1.000000e+02 ▇▁▁▁▁
somehs 0 1 10.17 6.200000e+00 0.00 5.90 9.40 13.90 7.220000e+01 ▇▂▁▁▁
hs 0 1 30.32 1.140000e+01 0.00 23.80 30.75 36.10 1.000000e+02 ▂▇▂▁▁
somecollege 0 1 21.58 8.600000e+00 0.00 17.50 21.30 24.70 1.000000e+02 ▆▇▁▁▁
associate 0 1 7.13 4.010000e+00 0.00 4.90 7.10 8.80 7.140000e+01 ▇▁▁▁▁
bachelor 0 1 14.90 9.710000e+00 0.00 8.80 12.95 19.22 1.000000e+02 ▇▂▁▁▁
grad 0 1 8.91 8.650000e+00 0.00 3.90 6.70 11.00 1.000000e+02 ▇▁▁▁▁
pov 0 1 14.95 1.133000e+01 0.00 6.50 12.10 21.22 6.590000e+01 ▇▅▂▁▁
hs_orless 0 1 47.48 1.675000e+01 0.00 37.92 48.65 59.10 1.000000e+02 ▁▃▇▃▁
urc2013 0 1 2.92 1.520000e+00 1.00 2.00 3.00 4.00 6.000000e+00 ▇▅▃▂▁
urc2006 0 1 2.97 1.520000e+00 1.00 2.00 3.00 4.00 6.000000e+00 ▇▅▃▂▁
aod 0 1 43.70 1.956000e+01 5.00 31.66 40.17 49.67 1.430000e+02 ▃▇▁▁▁

Notice how there is a column called n_missing about the number of values that are missing. It looks like our data is very complete and we do not have any missing data. This is also indicated by the complete_rate variable, which shows the ratio of completeness, in our case all variables have a value of 1 indicating they are fully complete.

The n_unqiue column shows us the number of unique values for each of our columns. We can see that there are 49 states represented in the data, and we know that the data should be of the contiguous states. Let’s take a look to see which states are included:

# A tibble: 49 x 1
   state               
   <chr>               
 1 Alabama             
 2 Arizona             
 3 Arkansas            
 4 California          
 5 Colorado            
 6 Connecticut         
 7 Delaware            
 8 District Of Columbia
 9 Florida             
10 Georgia             
11 Idaho               
12 Illinois            
13 Indiana             
14 Iowa                
15 Kansas              
16 Kentucky            
17 Louisiana           
18 Maine               
19 Maryland            
20 Massachusetts       
21 Michigan            
22 Minnesota           
23 Mississippi         
24 Missouri            
25 Montana             
26 Nebraska            
27 Nevada              
28 New Hampshire       
29 New Jersey          
30 New Mexico          
31 New York            
32 North Carolina      
33 North Dakota        
34 Ohio                
35 Oklahoma            
36 Oregon              
37 Pennsylvania        
38 Rhode Island        
39 South Carolina      
40 South Dakota        
41 Tennessee           
42 Texas               
43 Utah                
44 Vermont             
45 Virginia            
46 Washington          
47 West Virginia       
48 Wisconsin           
49 Wyoming             

Looks like “District of Columbia” is being included as a state. We can see that indeed Alaska and Hawaii are not included in the data.

Here is another method of looking at the data using the dfSummary() function of the summarytoolspackage. We need to copy and paste the output into the rmarkdown.

Dimensions: 876 x 50
Duplicates: 0

No Variable Stats / Values Freqs (% of Valid) Graph Valid Missing
1 id
[factor]
1. 1003.001
2. 1027.0001
3. 1033.1002
4. 1049.1003
5. 1055.001
6. 1069.0003
7. 1073.0023
8. 1073.1005
9. 1073.1009
10. 1073.101
[ 866 others ]
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
866 (98.9%)
876
(100%)
0
(0%)
2 value
[numeric]
Mean (sd) : 10.8 (2.6)
min < med < max:
3 < 11.2 < 23.2
IQR (CV) : 3.1 (0.2)
875 distinct values 876
(100%)
0
(0%)
3 fips
[factor]
1. 1003
2. 1027
3. 1033
4. 1049
5. 1055
6. 1069
7. 1073
8. 1089
9. 1097
10. 1101
[ 559 others ]
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
8 ( 0.9%)
1 ( 0.1%)
2 ( 0.2%)
1 ( 0.1%)
858 (98.0%)
876
(100%)
0
(0%)
4 lat
[numeric]
Mean (sd) : 38.5 (4.6)
min < med < max:
25.5 < 39.3 < 48.4
IQR (CV) : 6.6 (0.1)
876 distinct values 876
(100%)
0
(0%)
5 lon
[numeric]
Mean (sd) : -91.7 (15)
min < med < max:
-124.2 < -87.5 < -68
IQR (CV) : 18.5 (-0.2)
876 distinct values 876
(100%)
0
(0%)
6 state
[character]
1. California
2. Ohio
3. Illinois
4. Indiana
5. North Carolina
6. Pennsylvania
7. Michigan
8. Florida
9. Georgia
10. Texas
[ 39 others ]
85 ( 9.7%)
44 ( 5.0%)
38 ( 4.3%)
36 ( 4.1%)
35 ( 4.0%)
32 ( 3.7%)
30 ( 3.4%)
29 ( 3.3%)
28 ( 3.2%)
27 ( 3.1%)
492 (56.2%)
876
(100%)
0
(0%)
7 county
[character]
1. Jefferson
2. Cook
3. Hamilton
4. Lake
5. Los Angeles
6. Wayne
7. Washington
8. Cuyahoga
9. Jackson
10. Madison
[ 461 others ]
18 ( 2.1%)
12 ( 1.4%)
11 ( 1.3%)
11 ( 1.3%)
10 ( 1.1%)
10 ( 1.1%)
9 ( 1.0%)
7 ( 0.8%)
7 ( 0.8%)
7 ( 0.8%)
774 (88.4%)
876
(100%)
0
(0%)
8 city
[character]
1. Not in a city
2. New York
3. Cleveland
4. Baltimore
5. Chicago
6. Detroit
7. Milwaukee
8. New Haven
9. Philadelphia
10. Springfield
[ 597 others ]
103 (11.8%)
9 ( 1.0%)
6 ( 0.7%)
5 ( 0.6%)
5 ( 0.6%)
5 ( 0.6%)
5 ( 0.6%)
5 ( 0.6%)
5 ( 0.6%)
5 ( 0.6%)
723 (82.5%)
876
(100%)
0
(0%)
9 CMAQ  | Me [numeric] an (sd) : 8.4 (3)  | 60 min < med < max:
1.6 < 8.6 < 23.1
IQR (CV) : 3.7 (0.4)
1 distinct values | ![ ](tmp/ds0109.png) | 87 6  | 0
(100%)
(0%)
10 zcta
[factor]
1. 1022
2. 1103
3. 1201
4. 1608
5. 1832
6. 1840
7. 1863
8. 1904
9. 2113
10. 2119
[ 832 others ]
1 ( 0.1%)
2 ( 0.2%)
1 ( 0.1%)
2 ( 0.2%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
1 ( 0.1%)
864 (98.6%)
876
(100%)
0
(0%)
11 zcta_area
[numeric]
Mean (sd) : 183173481.9 (542598878.5)
min < med < max:
15459 < 37653560.5 < 8164820625
IQR (CV) : 145836906.5 (3)
842 distinct values 876
(100%)
0
(0%)
12 zcta_pop
[numeric]
Mean (sd) : 24227.6 (17772.2)
min < med < max:
0 < 22014 < 95397
IQR (CV) : 25207.8 (0.7)
837 distinct values 876
(100%)
0
(0%)
13 imp_a500
[numeric]
Mean (sd) : 24.7 (19.3)
min < med < max:
0 < 25.1 < 69.6
IQR (CV) : 36.5 (0.8)
816 distinct values 876
(100%)
0
(0%)
14 imp_a1000
[numeric]
Mean (sd) : 24.3 (18)
min < med < max:
0 < 24.5 < 67.5
IQR (CV) : 33.3 (0.7)
860 distinct values 876
(100%)
0
(0%)
15 imp_a5000
[numeric]
Mean (sd) : 19.9 (14.7)
min < med < max:
0.1 < 19.1 < 74.6
IQR (CV) : 23.3 (0.7)
870 distinct values 876
(100%)
0
(0%)
16 imp_a10000
[numeric]
Mean (sd) : 15.8 (13.8)
min < med < max:
0.1 < 12.4 < 72.1
IQR (CV) : 19.6 (0.9)
870 distinct values 876
(100%)
0
(0%)
17 imp_a15000
[numeric]
Mean (sd) : 13.4 (13.1)
min < med < max:
0.1 < 9.7 < 71.1
IQR (CV) : 17.3 (1)
870 distinct values 876
(100%)
0
(0%)
18 county_area
[numeric]
Mean (sd) : 3768701992.1 (6212829553.6)
min < med < max:
33703512 < 1690826566.5 < 51947229509
IQR (CV) : 1761655911.5 (1.6)
564 distinct values 876
(100%)
0
(0%)
19 county_pop
[numeric]
Mean (sd) : 687298.4 (1293488.7)
min < med < max:
783 < 280730.5 < 9818605
IQR (CV) : 642211 (1.9)
564 distinct values 876
(100%)
0
(0%)
20 log_dist_to_prisec
[numeric]
Mean (sd) : 6.2 (1.4)
min < med < max:
-1.5 < 6.4 < 10.5
IQR (CV) : 1.7 (0.2)
870 distinct values 876
(100%)
0
(0%)
21 log_pri_length_5000
[numeric]
Mean (sd) : 9.8 (1.1)
min < med < max:
8.5 < 10.1 < 12
IQR (CV) : 2.2 (0.1)
586 distinct values 876
(100%)
0
(0%)
22 log_pri_length_10000
[numeric]
Mean (sd) : 10.9 (1.1)
min < med < max:
9.2 < 11.2 < 13
IQR (CV) : 2 (0.1)
687 distinct values 876
(100%)
0
(0%)
23 log_pri_length_15000
[numeric]
Mean (sd) : 11.5 (1.1)
min < med < max:
9.6 < 11.7 < 13.6
IQR (CV) : 1.5 (0.1)
726 distinct values 876
(100%)
0
(0%)
24 log_pri_length_25000
[numeric]
Mean (sd) : 12.2 (1.1)
min < med < max:
10.1 < 12.5 < 14.4
IQR (CV) : 1.4 (0.1)
787 distinct values 876
(100%)
0
(0%)
25 log_prisec_length_500
[numeric]
Mean (sd) : 7 (1)
min < med < max:
6.2 < 6.2 < 9.4
IQR (CV) : 1.6 (0.1)
382 distinct values 876
(100%)
0
(0%)
26 log_prisec_length_1000
[numeric]
Mean (sd) : 8.6 (0.8)
min < med < max:
7.6 < 8.7 < 10.5
IQR (CV) : 1.6 (0.1)
591 distinct values 876
(100%)
0
(0%)
27 log_prisec_length_5000
[numeric]
Mean (sd) : 11.3 (0.8)
min < med < max:
8.5 < 11.4 < 12.8
IQR (CV) : 0.9 (0.1)
852 distinct values 876
(100%)
0
(0%)
28 log_prisec_length_10000
[numeric]
Mean (sd) : 12.4 (0.7)
min < med < max:
9.2 < 12.5 < 13.8
IQR (CV) : 1 (0.1)
867 distinct values 876
(100%)
0
(0%)
29 log_prisec_length_15000
[numeric]
Mean (sd) : 13 (0.7)
min < med < max:
9.6 < 13.1 < 14.4
IQR (CV) : 1 (0.1)
869 distinct values 876
(100%)
0
(0%)
30 log_prisec_length_25000
[numeric]
Mean (sd) : 13.8 (0.7)
min < med < max:
10.1 < 13.9 < 15.2
IQR (CV) : 1 (0.1)
870 distinct values 876
(100%)
0
(0%)
31 log_nei_2008_pm25_sum_10000
[numeric]
Mean (sd) : 4 (2.4)
min < med < max:
0 < 4.3 < 9.1
IQR (CV) : 3.5 (0.6)
828 distinct values 876
(100%)
0
(0%)
32 log_nei_2008_pm25_sum_15000
[numeric]
Mean (sd) : 4.7 (2.2)
min < med < max:
0 < 5 < 9.4
IQR (CV) : 2.9 (0.5)
855 distinct values 876
(100%)
0
(0%)
33 log_nei_2008_pm25_sum_25000
[numeric]
Mean (sd) : 5.7 (2.1)
min < med < max:
0 < 5.9 < 9.7
IQR (CV) : 2.6 (0.4)
860 distinct values 876
(100%)
0
(0%)
34 log_nei_2008_pm10_sum_10000
[numeric]
Mean (sd) : 4.3 (2.3)
min < med < max:
0 < 4.6 < 9.3
IQR (CV) : 3.4 (0.5)
829 distinct values 876
(100%)
0
(0%)
35 log_nei_2008_pm10_sum_15000
[numeric]
Mean (sd) : 5.1 (2.2)
min < med < max:
0 < 5.4 < 9.7
IQR (CV) : 2.8 (0.4)
855 distinct values 876
(100%)
0
(0%)
36 log_nei_2008_pm10_sum_25000
[numeric]
Mean (sd) : 6.1 (2)
min < med < max:
0 < 6.4 < 9.9
IQR (CV) : 2.4 (0.3)
860 distinct values 876
(100%)
0
(0%)
37 popdens_county
[numeric]
Mean (sd) : 551.8 (1711.5)
min < med < max:
0.3 < 156.7 < 26821.9
IQR (CV) : 470 (3.1)
564 distinct values 876
(100%)
0
(0%)
38 popdens_zcta
[numeric]
Mean (sd) : 1279.7 (2757.5)
min < med < max:
0 < 610.3 < 30418.8
IQR (CV) : 1281.4 (2.2)
840 distinct values 876
(100%)
0
(0%)
39 nohs
[numeric]
Mean (sd) : 7 (7.2)
min < med < max:
0 < 5.1 < 100
IQR (CV) : 6.1 (1)
215 distinct values 876
(100%)
0
(0%)
40 somehs
[numeric]
Mean (sd) : 10.2 (6.2)
min < med < max:
0 < 9.4 < 72.2
IQR (CV) : 8 (0.6)
230 distinct values 876
(100%)
0
(0%)
41 hs
[numeric]
Mean (sd) : 30.3 (11.4)
min < med < max:
0 < 30.8 < 100
IQR (CV) : 12.3 (0.4)
347 distinct values 876
(100%)
0
(0%)
42 somecollege
[numeric]
Mean (sd) : 21.6 (8.6)
min < med < max:
0 < 21.3 < 100
IQR (CV) : 7.2 (0.4)
240 distinct values 876
(100%)
0
(0%)
43 associate
[numeric]
Mean (sd) : 7.1 (4)
min < med < max:
0 < 7.1 < 71.4
IQR (CV) : 3.9 (0.6)
157 distinct values 876
(100%)
0
(0%)
44 bachelor
[numeric]
Mean (sd) : 14.9 (9.7)
min < med < max:
0 < 12.9 < 100
IQR (CV) : 10.4 (0.7)
301 distinct values 876
(100%)
0
(0%)
45 grad
[numeric]
Mean (sd) : 8.9 (8.6)
min < med < max:
0 < 6.7 < 100
IQR (CV) : 7.1 (1)
245 distinct values 876
(100%)
0
(0%)
46 pov
[numeric]
Mean (sd) : 15 (11.3)
min < med < max:
0 < 12.1 < 65.9
IQR (CV) : 14.7 (0.8)
345 distinct values 876
(100%)
0
(0%)
47 hs_orless
[numeric]
Mean (sd) : 47.5 (16.8)
min < med < max:
0 < 48.7 < 100
IQR (CV) : 21.2 (0.4)
464 distinct values 876
(100%)
0
(0%)
48 urc2013
[numeric]
Mean (sd) : 2.9 (1.5)
min < med < max:
1 < 3 < 6
IQR (CV) : 2 (0.5)
1 : 203 (23.2%)
2 : 163 (18.6%)
3 : 228 (26.0%)
4 : 123 (14.0%)
5 : 101 (11.5%)
6 : 58 ( 6.6%)
876
(100%)
0
(0%)
49 urc2006
[numeric]
Mean (sd) : 3 (1.5)
min < med < max:
1 < 3 < 6
IQR (CV) : 2 (0.5)
1 : 195 (22.3%)
2 : 162 (18.5%)
3 : 221 (25.2%)
4 : 127 (14.5%)
5 : 115 (13.1%)
6 : 56 ( 6.4%)
876
(100%)
0
(0%)
50 aod
[numeric]
Mean (sd) : 43.7 (19.6)
min < med < max:
5 < 40.2 < 143
IQR (CV) : 18 (0.4)
581 distinct values 876
(100%)
0
(0%)

We can see that for many variables there are many low values as the distribution shows two peaks, one near zero and another with a higher value. This is true for the imp variables (measures of development), the nei variables (measures of emission sources) and the road density variables. We can also see that the range of some of the variables is very large, in particular the area and population related variables.

Evaluate correlation among possible predictors

In prediction analyses, it is also useful to evaluate if any of the variables are correlated.

Intuitively we can expect some of our variables to be correlated.

Let’s first take a look at all of our numeric variabels with thecorrplot package: The corrplot package is another option to look at correlation among possible predictors. This is a great option if we have many predictors. First we need to create a correlation matrix using the cor() function of the stats package (which is loaded automatically).

Using ggcorplot

We can see that the the development variables (imp) variables are correlated with each other as we might expect. We also see that the road density variables seem to be correlated with each other, and the emission variables seem to be correlated with each other. We can take a closer look using the ggcorr() function and the ggpairs() function of the GGally package. To select our variables of interest we can use the select() function with the contains() function of the tidyr package.

First let’s look at the imp/development variables.

Indeed, we can see that imp_a1000 and imp_a500 are perfectly correlated, as well as imp_a10000, imp_a15000.

Now let’s take a look at the road density data:

We can see that while some of the road density variables are highly correlated with one another (or anti-correlated), there arr some variables that are less correlated with one another.

Finally let’s look at the emission variables.

We would also expect the population density data might correlate with some of these variables. Let’s take a look.

Interesting, so these variables don’t appear to be highly correlated, therefore we might need variables from each of the categories to predict our monitor PM2.5 pollution values.

We seem to have some pretty extreme population values though, so let’s see what happens when we take the log value.

Indeed this increased the correlation, but variables from each of these categories may still prove to be useful for prediction.

Data Analysis

Now that we have a sense of what our data is like we can get started with data analysis.

The machine learning process

There are two major types of machine learning:

  1. Unsupervised
  2. Supervised

Unsupervised learning is used to learn about the structure of the data without knowing much about the data. We let the data reveal properties about itself. Examples include clustering the data into groups or reducing the dimensionality of the data using methods like principal component analysis (which we will describe in more detail later) to capture patterns of varaince within the data.

[source]

In contrast, in supervised learning we have some knowlege about the data that we want to use to create a model to be able to generalize about other similar data.

There are two distinct goals of supervised machine learning:

  1. Prediction
  2. Classification

[source]

We will be performing a prediction analysis (which is also refered to as regression), which aims to predict continuous outcome variables given a number of predictors/explanatory variables/features/parameters, as we have already described.

Classification on the other hand aims to discern or predict group identity for a categorical outcome based on a number of predictors/explanatory variables/features/parameters.

The overall process is the same in either case and involes the following steps (which will each be explained in detail):

  1. Data exploration

We have already performed this step to get a sense of the data. It is important to know if we have NA values, to understand the class of variables, and if to determine if there are any redudant variables that might need to removed.

  1. Data splitting

The data needs to be split into two peices: a training set and a testing set. The training set will be used to optimize the model, while the testing set will be used to evaluate model performance.

  1. Variable assignment and pre-processing

Both the training and testing data needs to be processed so that the data is compatible and optimized to be used with the model. This involves assigning variables to specific roles within the model and preprocessing like scaling variables and removing redudant variables.

  1. Model specification, fitting, tuning and performance evaluation using the training data

The model needs to first be fit to the training data. First the method or algorithm in which the model will be fit is specified (regression, random forests etc.). Then in both classification and prediction, the model is fit to the traing data and the explanatory variables are used to estimate numeric values (in the case of prediction) or categorical values (in the case of classification) of the outcome variable of interest. If the model fits well than these estimated values will be very similar to the true outcome variable values. If the model does not fit well, than these estimates will be more dissimal from the true outcome variable. In this case, aspects about the model may need to be modified to improve the similarity of the estimates with that ofthe true outcome values. One way to optimize model performance is a process called tuning in which different model hyperparameter options are tested to determine the best option for model performance.

  1. Overall model performance evaluation

Model performance is assessed as the similarity between the estimates of the outcome variable produced by the model and the true outcome variable values. This is done typically as an interative process with the training data along side modification of the model untill the performance using the training data is satisfactory. At this point, the final model performance is assessed using the testing data. This then gives an estimate about how well the model will predict or classify the outcome variable of interest with new independent data. Ideally one would also perform an evaluation with independent data to provide a sense of how generalizable the model is to other data sources.

The tidymodels ecosystem

To perform our analysis we will be using the tidymodels suite of packages. You may be familiar with the older packages caret or mlr which are also for machine learning and modeling but are not a part of the tidyverse. Max Kuhn describes tidymodels like this:

“Other packages, such as caret and mlr, help to solve the R model API issue. These packages do a lot of other things too: preprocessing, model tuning, resampling, feature selection, ensembling, and so on. In the tidyverse, we strive to make our packages modular and parsnip is designed only to solve the interface issue. It is not designed to be a drop-in replacement for caret. The tidymodels package collection, which includes parsnip, has other packages for many of these tasks, and they are designed to work together. We are working towards higher-level APIs that can replicate and extend what the current model packages can do.”

There are many packages in the tidymodels ecosystem which assist with the various steps of the machine learning process:

This is a depiction of how these tools help perform the overall machine learning process:

The major benefits of tidymodels

  1. Standardized workflow/format/notation across different types of algorithms

Different notations are required for different algorithms as the algorithms have been developed by many different people. This would require the painstaking process of reformatting the data to be compatible with each algorithm if multiple algorithms were tested.

  1. Can easily modify preprocessing, algorithm choice, and hyper-parameter tuning making optimization easy

Modiying a piece of the overall process is now much easier than before because many of the steps are specified using the tidymodel packages in a convenient manner. Thus the entire process can be rerun after a simple change to preprocessing without much difficulty.

Splitting the Data

The first step after data exploration in machine learning analysis is to split the data into training and testing datasets.

The training dataset will be used to build and tune our model. This is the data that the model “learns” on.

The testing set will be used to evaluate the performance of our model in a more generalizable way. What do we mean by “generalizable”?

Remember that our main goal is to use our model to be able to predict air pollution levels in areas where there are no gravimetric monitors. Therefore, if our model is super good at predicting air pollution with the data that we use to build it, it might not do the best job for the areas where there are few to no monitors. This would cause us to have really good prediction accuracy and we might assume that we were going to do a good job estimating air pollution any time we use our model, but in fact this would likely not be the case. This situation is what we call overfitting .

Overfitting happens when we end up modeling not only the major relationships in our data but also the noise within our data.

[source]

If we get fairly good prediction with our testing set then we will know that our model can be applied to other data and will perform fairly well. We will discuss this more later.

We will not touch the testing set until we have completed optimizing our model with the training set. This will allow us to have a less biased evaluation of how well our model can do with other data besides the data used in the training set to build the model. Ideally you would also want a completely independent dataset to further test the performance of your model.

Here is a great description of the differences between testing and training datasets.

We will use the rsample package to perform this step.

Theinitial_split() function allows us to specify how we want to split our data. Typically data is split into 3/4 for training and 1/4 for testing.This is the default proportion and does not need to be specified. However you can change the proportion using the prop argument, which we will do that here for illustrative purposes. You can also specify a variable to stratify by with the strata argument. This is useful if you have imbalanced categorical variables and you would like to intentionally make sure that there are similar number of samples of the rarer categories in both the testing and training sets. Otherwise the split is performed randomly.

The strata argument causes the random sampling to be conducted within the stratification variable. The can help ensure that the number of data points in the training data is equivalent to the proportions in the original data set.

In the case with our dataset, perhaps we would like our training set to have similar proportions of monitors from each of the states as in the initial data. This might be useful if we want our model to be generalizable across all of the states.

We can see that indeed there are different proportions of monitors in each state by using the count() function of the dpyr package.

# A tibble: 49 x 2
   state                    n
   <chr>                <int>
 1 Alabama                 24
 2 Arizona                 17
 3 Arkansas                16
 4 California              85
 5 Colorado                15
 6 Connecticut             14
 7 Delaware                 7
 8 District Of Columbia     3
 9 Florida                 29
10 Georgia                 28
11 Idaho                    7
12 Illinois                38
13 Indiana                 36
14 Iowa                    20
15 Kansas                  10
16 Kentucky                22
17 Louisiana               17
18 Maine                    1
19 Maryland                15
20 Massachusetts           16
21 Michigan                30
22 Minnesota               17
23 Mississippi             12
24 Missouri                13
25 Montana                 16
26 Nebraska                 7
27 Nevada                   4
28 New Hampshire            7
29 New Jersey              23
30 New Mexico              10
31 New York                24
32 North Carolina          35
33 North Dakota             4
34 Ohio                    44
35 Oklahoma                10
36 Oregon                  17
37 Pennsylvania            32
38 Rhode Island             5
39 South Carolina          14
40 South Dakota             9
41 Tennessee                3
42 Texas                   27
43 Utah                    14
44 Vermont                  4
45 Virginia                20
46 Washington               8
47 West Virginia           14
48 Wisconsin               21
49 Wyoming                 12

If our dataset were large enough it might be nice then to stratify by state, but our data is unfortunately not large enough. We will show how one would do this though for illustrative purposes. This option is often more important for classification applications of machine learning than it is for prediction.

Since the split is performed randomly, it is a good idea to use the set.seed() base function to ensure that if your rerun your code that your split will be the same next time. We can see the number of monitors in our training, testing, and original data by typing in the name of our split object. The result will look like this: <training data sample number, testing data sample number, original sample number>

<Analysis/Assess/Total>
<584/292/876>

Importantly the initial_split function only determines what rows of our pm data frame should be assigned for training or testing, it does not actually split the data.

To extract the testing and training data we can use the training() and testing() functions also of the rsample package.

# A tibble: 48 x 2
   state                    n
   <chr>                <int>
 1 Alabama                 18
 2 Arizona                 12
 3 Arkansas                14
 4 California              54
 5 Colorado                12
 6 Connecticut              8
 7 Delaware                 6
 8 District Of Columbia     2
 9 Florida                 18
10 Georgia                 17
# … with 38 more rows
# A tibble: 48 x 2
   state                    n
   <chr>                <int>
 1 Alabama                  6
 2 Arizona                  5
 3 Arkansas                 2
 4 California              31
 5 Colorado                 3
 6 Connecticut              6
 7 Delaware                 1
 8 District Of Columbia     1
 9 Florida                 11
10 Georgia                 11
# … with 38 more rows

Variable Role Assignment and Preprocessing

In tidymodels we will create a recipe, which is a standardized format for a sequence of steps for processing the data.

This can be very useful because it makes testing out different pre-processing steps or different algorithms with the same pre-processing very easy and reproducible.

Creating a recipe specifies how a dataframe of predictors should be created - it specifies what variables to be used and the preprocessing steps but it does not execute these steps or create the dataframe of predictors.

List the ingredients / specify the variables with the recipe() function

The first thing to do to create a recipe is to specify which variables we will be using as our outcome and predictors using the recipe() function. In terms of the metaphor of baking, we can think of this as listing our ingredients. The naming convention for recipe object names is *_rec or rec.

In our case recall that our value variable, which is the average annual gravimetric monitor PM2.5 concentration in ug/m3. Our predictors are all the other variables except the monitor ID, which is an id variable.

The reason not to include this variable is because this variable includes the county number and a number designating which particular monitor the values came from of the monitors there are in that county. Since this number is arbitrary and the county information is also given in the data, and the fact that each monitor only has one value in the value variable, nothing is gained by including this variable and it may instead introduce noise. However, it is useful to keep this data to take a look at what is happening later. We will show you what to do in this case in just a bit.

The simplest recipe with no preprocessing steps, would be to simply list the outcome and predictor variables.

We can do so in two ways:

  1. Using formula notation
  2. Assigning roles to each variable

Let’s look at the first way using formula notation, which looks like this:

outcome(s) ~ predictor(s)

If in the case of multiple predictors or a multivariate situation with two outcomes, use a plus sign

outcome1 + outcome2 ~ predictor1 + predictor2

If we want to include all predictors we can use a period like so:

outcome_variable_name ~ .

Now with our data we will start by making a recipe for our training data. In the simplest case we might use all predictors like this:

Data Recipe

Inputs:

      role #variables
   outcome          1
 predictor         49

However, to deal with the id variable we could use the update_role() function of the recipes package. This option works well with the newer workflows package, however id variabels are often dropped from analyses that do not use this newer package as they can make the process difficult with using the parsnip package alone due to the fact that new levels (or possible values) may be introduced with the testing data.

Data Recipe

Inputs:

        role #variables
 id variable          1
     outcome          1
   predictor         48

We could also specify the outcome and predictors in the same way as the id variable. Please see here for examples of other roles for variables. The role can be actually be any value.

The order is important here, as we first make all variables predictors and then override this role for the outcome and id variable. We will use the everything() function of the dplyr package to start with all of the variables in train_pm.

Data Recipe

Inputs:

        role #variables
 id variable          1
     outcome          1
   predictor         48

If we want to take a look at our formula from our recipe we can do use the formula() function of the stats package.

value ~ fips + lat + lon + state + county + city + CMAQ + zcta + 
    zcta_area + zcta_pop + imp_a500 + imp_a1000 + imp_a5000 + 
    imp_a10000 + imp_a15000 + county_area + county_pop + log_dist_to_prisec + 
    log_pri_length_5000 + log_pri_length_10000 + log_pri_length_15000 + 
    log_pri_length_25000 + log_prisec_length_500 + log_prisec_length_1000 + 
    log_prisec_length_5000 + log_prisec_length_10000 + log_prisec_length_15000 + 
    log_prisec_length_25000 + log_nei_2008_pm25_sum_10000 + log_nei_2008_pm25_sum_15000 + 
    log_nei_2008_pm25_sum_25000 + log_nei_2008_pm10_sum_10000 + 
    log_nei_2008_pm10_sum_15000 + log_nei_2008_pm10_sum_25000 + 
    popdens_county + popdens_zcta + nohs + somehs + hs + somecollege + 
    associate + bachelor + grad + pov + hs_orless + urc2013 + 
    urc2006 + aod
<environment: 0x7fdfab277ab8>

We can also view our recipe in more detail using the base summary() function.

# A tibble: 50 x 4
   variable type    role        source  
   <chr>    <chr>   <chr>       <chr>   
 1 id       nominal id variable original
 2 value    numeric outcome     original
 3 fips     nominal predictor   original
 4 lat      numeric predictor   original
 5 lon      numeric predictor   original
 6 state    nominal predictor   original
 7 county   nominal predictor   original
 8 city     nominal predictor   original
 9 CMAQ     numeric predictor   original
10 zcta     nominal predictor   original
# … with 40 more rows

List the preprocessing steps using the step functions of the recipe package

The other thing the recipes package allows for is specifying pre-processing steps using a variety of step*() functions.

This link and this link show the many options for recipe step functions.

There are step functions for a variety of purposes:

  1. Imputation – which means filling in missing values based on the existing data
  2. Transformation – which means changing all values of a variable in the same way, typically to make it more normal or easier to interpret)
  3. Discretization – which means converting continuous values into discrete or nominal values - binning for example to reduce the number of possible levels) (However this is generally not advisable!)
  4. Encoding / Creating Dummy Variables – which means creating a numeric code for categorical variables More on Dummy Variables and one hot encoding
  5. Data type conversions – which means changing from integer to factor or numeric to date etc.
  6. Interaction term addition to the model – which means that we would be modeling for predictors that would influence the capacity of each other to predict the outcome
  7. Normalization – which means centering and scaling the data to a similar range of values
  8. Dimensionality Reduction/ Signal Extraction – which means mathematically obtaining a new smaller set of variables that capture the variation or signal in the original variables (ex. Principal Component Analysis and Independent Component Analysis)
  9. Filtering – Filtering options for removing variables (ex. remove variables that are highly correlated to others or remove variables with very little variance and therefore likely little predictive capacity)
  10. Row operations – which means performing functions on the values within the rows (ex. rearranging, filtering, imputing)
  11. Checking functions – Sanity checks to look for missing values, to look at the variable classes etc.

All of the step functions look like step_* except for the check functions which look like check_*.

There are several ways to select what variables to apply steps to:
1) tidyselect methods: contains(), matches(), starts_with(), ends_with(), everything(), num_range()
2) based on the type: all_nominal(), all_numeric() , has_type() 3) based on the role: all_predictors(), all_outcomes(), has_role() 4) name - use the actual name of the variable/variables of interest

Let’s try adding some steps to our recipe.

We might consider log transforming our population and area variables (that aren’t densities) - let’s take a look at the range of these variables.

$zcta_area
[1]      15459 8164820625

$zcta_pop
[1]     0 95397

$county_area
[1]    33703512 51947229509

$county_pop
[1]     783 9818605

We can see that the range for each of these variables is quite large, we can log transform this data using the step_log() function of the recipes package.

We would also want to potentially one hot encode some of our categorical variables so that they can be used with certain algorithms. We can do this with the step_dummy() function and the one_hot = TRUE argument. Our fips variable includes a numeric code for state and county - and therefore is essentially a proxy for county. Since we already have county, we will just use it and keep the fips id as another ID variable.

We can remove the fips variable from the predictors using update_role() to make sure that the role is no longer "predictor". We can make the role anything we want actually, so we will keep it something identifiable.

We might also want to remove variables that appear to be redundant and are highly correlated with others, as we know from our exploratory data analysis that many of our variables are correlated with one another. We can do this using the step_corr() function.

It is also a good idea to remove variables with near-zero variance, which can be done with the step_nzv() function. Variables have low variance if all the values are very similar, the values are very sparse, or if they are highly imbalanced.

Examples where you might have near-zero variance variables include: ### I Struggled to find links for this… to back up my statements###

  1. Similar Values - If the population density was nearly the same for every zcta that contained a monitor, then knowing the population density near our monitorwould contribute little to our model in assisting us to predict monitor air pollution values.
  2. Sparse Data - If all of the monitors were in locations where the populations did not attend graduate school, then these values would mostly be zero, again this would do very little to help us distinguish our air pollution monitors.When many of the values are zero this is also called sparse data.
  3. Imbalanced Data If nearly all of the monitors were located in one particular state, and all the others only had one monitor each, then the real predictive value would simply be in knowing if a monitor is located in that particular state or not. In this case we don’t want to remove our variable, we just want to simplify it.

It is important to add the steps to the recipe in an order that makes sense just like with a cooking recipe.

Thus first we are going to create numeric values for our categorical variables, then we will look at correlation and near-zero variance. We don’t want to remove some of our variables, like the CMAQ and aod variables so we can make sure they are kept in the model by excluding them from those steps. If we specifically wanted to remove a predictor we could use step_rm().

Data Recipe

Inputs:

        role #variables
   county id          1
 id variable          1
     outcome          1
   predictor         47

Operations:

Dummy variables from state, county, city, zcta
Correlation filter on all_predictors, -, CMAQ, -, aod
Sparse, unbalanced variable filter on all_predictors, -, CMAQ, -, aod

Running the preprocessing

The next major function of the recipes package is prep().

This function updates the recipe object based on the training data. It estimates parameters (estimating the required quantities and statistics required by the steps for the variables) for preprocessing and updates the model terms, as some of the predictors may be removed, this allows the recipe to be ready to use on other datasets. It doesn’t necessarily actually execute the preprocessing itself, however we will specify in argument for it to do this so that we can take a look at the preprocessed data.

There are some important arguments to know about: 1) training - you must supply a training data set to estimate parameters for preprocessing operations (recipe steps) - this may already be included in your recipe - as is the case for us 2) fresh - if TRUE - will retrain and estimate parameters for any previous steps that were already prepped if you add more steps to the recipe 3) verbose - if TRUE shows the progress as the steps are evaluated and the size of the preprocessed training set 4) retain - if TRUE then the preprocessed training set will be saved within the recipe (as template). This is good if you are likely to add more steps and don’t want to rerun the prep() on the previous steps. However this can make the recipe size large. This is necessary if you want to actually look at the preprocessed data.

oper 1 step dummy [training] 
oper 2 step corr [training] 
oper 3 step nzv [training] 
The retained training set is ~ 0.26 Mb  in memory.
[1] "var_info"       "term_info"      "steps"          "template"      
[5] "levels"         "retained"       "tr_info"        "orig_lvls"     
[9] "last_term_info"

There are also lots of useful things to checkout in the output of prep(). You can see: 1) the steps that were run
2) the variable info (var_info)
3) the model term_info 4) the new levels of the variables 5) the original levels of the variables orig_lvls
6) info about the training data set size and completeness (tr_info)

Note: You may see the prep.recipe() function in material that you read about the recipes package. This is referring to the prep() function of the recipes package.

Extracting the preprocessed training data

Since we retained our preprocessed training data, we can take a look at it like by using the juice() function of the recipes package like this:

Rows: 584
Columns: 36
$ id                          <fct> 1003.001, 1027.0001, 1033.1002, 1055.001,…
$ value                       <dbl> 9.597647, 10.800000, 11.212174, 12.375394…
$ fips                        <fct> 1003, 1027, 1033, 1055, 1069, 1073, 1073,…
$ lat                         <dbl> 30.49800, 33.28126, 34.75878, 33.99375, 3…
$ lon                         <dbl> -87.88141, -85.80218, -87.65056, -85.9910…
$ CMAQ                        <dbl> 8.098836, 9.766208, 9.402679, 9.241744, 9…
$ zcta_area                   <dbl> 190980522, 374132430, 16716984, 154069359…
$ zcta_pop                    <dbl> 27829, 5103, 9042, 20045, 30217, 9010, 16…
$ imp_a500                    <dbl> 0.01730104, 1.96972318, 19.17301038, 16.4…
$ imp_a15000                  <dbl> 1.4386207, 0.3359198, 5.2472094, 5.161210…
$ county_area                 <dbl> 4117521611, 1564252280, 1534877333, 13856…
$ county_pop                  <dbl> 182265, 13932, 54428, 104430, 101547, 658…
$ log_dist_to_prisec          <dbl> 4.648181, 7.219907, 5.760131, 5.261457, 7…
$ log_pri_length_5000         <dbl> 8.517193, 8.517193, 8.517193, 9.066563, 8…
$ log_pri_length_25000        <dbl> 11.32735, 10.12663, 10.15769, 12.01356, 1…
$ log_prisec_length_500       <dbl> 7.295356, 6.214608, 8.611945, 8.740680, 6…
$ log_prisec_length_1000      <dbl> 8.195119, 7.600902, 9.735569, 9.627898, 7…
$ log_prisec_length_5000      <dbl> 10.815042, 10.170878, 11.770407, 11.72888…
$ log_prisec_length_10000     <dbl> 11.886803, 11.405543, 12.840663, 12.76827…
$ log_nei_2008_pm10_sum_15000 <dbl> 2.26783411, 3.31111648, 6.70127741, 4.462…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.628728, 3.311116, 7.148858, 4.678311, 3…
$ popdens_county              <dbl> 44.265706, 8.906492, 35.460814, 75.367038…
$ popdens_zcta                <dbl> 145.7164307, 13.6395554, 540.8870404, 130…
$ nohs                        <dbl> 3.3, 11.6, 7.3, 4.3, 5.8, 7.1, 2.7, 11.1,…
$ somehs                      <dbl> 4.9, 19.1, 15.8, 13.3, 11.6, 17.1, 6.6, 1…
$ hs                          <dbl> 25.1, 33.9, 30.6, 27.8, 29.8, 37.2, 30.7,…
$ somecollege                 <dbl> 19.7, 18.8, 20.9, 29.2, 21.4, 23.5, 25.7,…
$ associate                   <dbl> 8.2, 8.0, 7.6, 10.1, 7.9, 7.3, 8.0, 4.1, …
$ bachelor                    <dbl> 25.3, 5.5, 12.7, 10.0, 13.7, 5.9, 17.6, 7…
$ grad                        <dbl> 13.5, 3.1, 5.1, 5.4, 9.8, 2.0, 8.7, 2.9, …
$ pov                         <dbl> 6.1, 19.5, 19.0, 8.8, 15.6, 25.5, 7.3, 8.…
$ hs_orless                   <dbl> 33.3, 64.6, 53.7, 45.4, 47.2, 61.4, 40.0,…
$ urc2013                     <dbl> 4, 6, 4, 4, 4, 1, 1, 1, 1, 1, 2, 3, 3, 3,…
$ aod                         <dbl> 37.363636, 34.818182, 36.000000, 43.41666…
$ state_California            <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ city_Not.in.a.city          <dbl> 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,…

For easy comparison sake - here is our original data: #### {.scrollable }

Rows: 876
Columns: 50
$ id                          <fct> 1003.001, 1027.0001, 1033.1002, 1049.1003…
$ value                       <dbl> 9.597647, 10.800000, 11.212174, 11.659091…
$ fips                        <fct> 1003, 1027, 1033, 1049, 1055, 1069, 1073,…
$ lat                         <dbl> 30.49800, 33.28126, 34.75878, 34.28763, 3…
$ lon                         <dbl> -87.88141, -85.80218, -87.65056, -85.9683…
$ state                       <chr> "Alabama", "Alabama", "Alabama", "Alabama…
$ county                      <chr> "Baldwin", "Clay", "Colbert", "DeKalb", "…
$ city                        <chr> "Fairhope", "Ashland", "Muscle Shoals", "…
$ CMAQ                        <dbl> 8.098836, 9.766208, 9.402679, 8.534772, 9…
$ zcta                        <fct> 36532, 36251, 35660, 35962, 35901, 36303,…
$ zcta_area                   <dbl> 190980522, 374132430, 16716984, 203836235…
$ zcta_pop                    <dbl> 27829, 5103, 9042, 8300, 20045, 30217, 90…
$ imp_a500                    <dbl> 0.01730104, 1.96972318, 19.17301038, 5.78…
$ imp_a1000                   <dbl> 1.4096021, 0.8531574, 11.1448962, 3.86764…
$ imp_a5000                   <dbl> 3.3360118, 0.9851479, 15.1786154, 1.23114…
$ imp_a10000                  <dbl> 1.9879187, 0.5208189, 9.7253870, 1.031646…
$ imp_a15000                  <dbl> 1.4386207, 0.3359198, 5.2472094, 0.973044…
$ county_area                 <dbl> 4117521611, 1564252280, 1534877333, 20126…
$ county_pop                  <dbl> 182265, 13932, 54428, 71109, 104430, 1015…
$ log_dist_to_prisec          <dbl> 4.648181, 7.219907, 5.760131, 3.721489, 5…
$ log_pri_length_5000         <dbl> 8.517193, 8.517193, 8.517193, 8.517193, 9…
$ log_pri_length_10000        <dbl> 9.210340, 9.210340, 9.274303, 10.409411, …
$ log_pri_length_15000        <dbl> 9.630228, 9.615805, 9.658899, 11.173626, …
$ log_pri_length_25000        <dbl> 11.32735, 10.12663, 10.15769, 11.90959, 1…
$ log_prisec_length_500       <dbl> 7.295356, 6.214608, 8.611945, 7.310155, 8…
$ log_prisec_length_1000      <dbl> 8.195119, 7.600902, 9.735569, 8.585843, 9…
$ log_prisec_length_5000      <dbl> 10.815042, 10.170878, 11.770407, 10.21420…
$ log_prisec_length_10000     <dbl> 11.88680, 11.40554, 12.84066, 11.50894, 1…
$ log_prisec_length_15000     <dbl> 12.205723, 12.042963, 13.282656, 12.35366…
$ log_prisec_length_25000     <dbl> 13.41395, 12.79980, 13.79973, 13.55979, 1…
$ log_nei_2008_pm25_sum_10000 <dbl> 0.318035438, 3.218632928, 6.573127301, 0.…
$ log_nei_2008_pm25_sum_15000 <dbl> 1.967358961, 3.218632928, 6.581917457, 3.…
$ log_nei_2008_pm25_sum_25000 <dbl> 5.067308, 3.218633, 6.875900, 4.887665, 4…
$ log_nei_2008_pm10_sum_10000 <dbl> 1.35588511, 3.31111648, 6.69187313, 0.000…
$ log_nei_2008_pm10_sum_15000 <dbl> 2.26783411, 3.31111648, 6.70127741, 3.350…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.628728, 3.311116, 7.148858, 5.171920, 4…
$ popdens_county              <dbl> 44.265706, 8.906492, 35.460814, 35.330814…
$ popdens_zcta                <dbl> 145.716431, 13.639555, 540.887040, 40.718…
$ nohs                        <dbl> 3.3, 11.6, 7.3, 14.3, 4.3, 5.8, 7.1, 2.7,…
$ somehs                      <dbl> 4.9, 19.1, 15.8, 16.7, 13.3, 11.6, 17.1, …
$ hs                          <dbl> 25.1, 33.9, 30.6, 35.0, 27.8, 29.8, 37.2,…
$ somecollege                 <dbl> 19.7, 18.8, 20.9, 14.9, 29.2, 21.4, 23.5,…
$ associate                   <dbl> 8.2, 8.0, 7.6, 5.5, 10.1, 7.9, 7.3, 8.0, …
$ bachelor                    <dbl> 25.3, 5.5, 12.7, 7.9, 10.0, 13.7, 5.9, 17…
$ grad                        <dbl> 13.5, 3.1, 5.1, 5.8, 5.4, 9.8, 2.0, 8.7, …
$ pov                         <dbl> 6.1, 19.5, 19.0, 13.8, 8.8, 15.6, 25.5, 7…
$ hs_orless                   <dbl> 33.3, 64.6, 53.7, 66.0, 45.4, 47.2, 61.4,…
$ urc2013                     <dbl> 4, 6, 4, 6, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ urc2006                     <dbl> 5, 6, 4, 5, 4, 4, 1, 1, 1, 1, 1, 1, 1, 2,…
$ aod                         <dbl> 37.36364, 34.81818, 36.00000, 33.08333, 4…

Notice how we only have 36 variables now instead of 50! Two of these are our ID variables (fips and the actual monitor ID (id)) and one is our outcome (value). Thus we only have 33 predictors now. We can also see that variables that we no longer have any categorical variables. Variables like state are gone and only state_California remains as it was the only state identity to have nonzero variance. We can see that California had the largest number of monitors compared to the other states. We can also see that there were more monitors listed as "Not in a city" than any city.

# A tibble: 49 x 2
   state                    n
   <chr>                <int>
 1 Alabama                 24
 2 Arizona                 17
 3 Arkansas                16
 4 California              85
 5 Colorado                15
 6 Connecticut             14
 7 Delaware                 7
 8 District Of Columbia     3
 9 Florida                 29
10 Georgia                 28
# … with 39 more rows
# A tibble: 607 x 2
   city            n
   <chr>       <int>
 1 Aberdeen        1
 2 Akron           2
 3 Albany          3
 4 Albuquerque     2
 5 Alexandria      1
 6 Allen Park      1
 7 Altamont        1
 8 Alton           1
 9 Amarillo        1
10 Anadarko        1
# … with 597 more rows

Note: Recall that you must specify retain = TRUE argument of the prep() function to use juice().

Extracting the preprocessed testing data

Accroding to the tidymodels documentation:

bake() takes a trained recipe and applies the operations to a data set to create a design matrix. for example: it applies the centering to new data sets using these means used to create the recipe

If you wanted to look at the preprocessed testing data you would use the bake() function of the recipes package. (You generally want to leave your testing data alone, but it is good to look for issues like the introduction of NA values).

Rows: 292
Columns: 36
$ id                          <fct> 1049.1003, 1073.101, 1073.2006, 1089.0014…
$ value                       <dbl> 11.659091, 13.114545, 12.228125, 12.23294…
$ fips                        <fct> 1049, 1073, 1073, 1089, 1103, 1121, 4013,…
$ lat                         <dbl> 34.28763, 33.54528, 33.38639, 34.68767, 3…
$ lon                         <dbl> -85.96830, -86.54917, -86.81667, -86.5863…
$ CMAQ                        <dbl> 8.534772, 9.303766, 10.235612, 9.343611, …
$ zcta_area                   <dbl> 203836235, 148994881, 56063756, 46963946,…
$ zcta_pop                    <dbl> 8300, 14212, 32390, 21297, 30545, 7713, 5…
$ imp_a500                    <dbl> 5.78200692, 0.06055363, 42.42820069, 23.2…
$ imp_a15000                  <dbl> 0.9730444, 2.9956557, 12.7487614, 10.3555…
$ county_area                 <dbl> 2012662359, 2878192209, 2878192209, 20761…
$ county_pop                  <dbl> 71109, 658466, 658466, 334811, 119490, 82…
$ log_dist_to_prisec          <dbl> 3.721489, 7.301545, 4.721755, 4.659519, 6…
$ log_pri_length_5000         <dbl> 8.517193, 9.683336, 10.737240, 8.517193, …
$ log_pri_length_25000        <dbl> 11.90959, 12.53777, 12.99669, 11.47391, 1…
$ log_prisec_length_500       <dbl> 7.310155, 6.214608, 7.528913, 8.760549, 6…
$ log_prisec_length_1000      <dbl> 8.585843, 7.600902, 9.342290, 9.543183, 8…
$ log_prisec_length_5000      <dbl> 10.214200, 11.262645, 11.713190, 11.48606…
$ log_prisec_length_10000     <dbl> 11.50894, 12.14101, 12.53899, 12.68440, 1…
$ log_nei_2008_pm10_sum_15000 <dbl> 3.3500444, 6.6241114, 5.8268686, 3.861625…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.1719202, 7.5490587, 8.8205542, 5.219092…
$ popdens_county              <dbl> 35.330814, 228.777633, 228.777633, 161.26…
$ popdens_zcta                <dbl> 40.718962, 95.385827, 577.735106, 453.475…
$ nohs                        <dbl> 14.3, 7.2, 0.8, 1.2, 4.8, 16.7, 19.1, 6.4…
$ somehs                      <dbl> 16.7, 12.2, 2.6, 3.1, 7.8, 33.3, 15.6, 9.…
$ hs                          <dbl> 35.0, 32.2, 12.9, 15.1, 28.7, 37.5, 26.5,…
$ somecollege                 <dbl> 14.9, 19.0, 17.9, 20.5, 25.0, 12.5, 18.0,…
$ associate                   <dbl> 5.5, 6.8, 5.2, 6.5, 7.5, 0.0, 6.0, 8.8, 3…
$ bachelor                    <dbl> 7.9, 14.8, 35.5, 30.4, 18.2, 0.0, 10.6, 1…
$ grad                        <dbl> 5.8, 7.7, 25.2, 23.3, 8.0, 0.0, 4.1, 5.7,…
$ pov                         <dbl> 13.8, 10.5, 2.1, 5.2, 8.3, 18.8, 21.4, 14…
$ hs_orless                   <dbl> 66.0, 51.6, 16.3, 19.4, 41.3, 87.5, 61.2,…
$ urc2013                     <dbl> 6, 1, 1, 3, 4, 5, 1, 2, 5, 4, 4, 6, 6, 1,…
$ aod                         <dbl> 33.08333, 42.45455, 44.25000, 42.41667, 4…
$ state_California            <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,…
$ city_Not.in.a.city          <dbl> NA, NA, NA, NA, NA, NA, 0, NA, NA, NA, NA…

Notice that our city_Not.in.a.city variable seems to be NA values. Why might that be?

Ah! Perphas it is because some of our levels were not previously seen in the training set!

Let’s take a look using the set operations of the dplyr package. We can take a look at cities that were different between the test and training set.

[1] 376   1
[1] 51  1

Indeed, there are lots of different cities in our test data that are not in our training data!

Maybe remove this?: Thus we need to update our original recipe to include a very important step function called step_novel() this helps in cases like this were there are new factors in our testing set that were not in our training set. It is a good idea to include this in most of your recipes where you have a categorical variables with many distinct values. This step needs to come before we create dummy variables. However, we are also creating a dummy variable from this, which still results in a problem.

Let’s modify the city variable to be values of in a city or not in a city using the if_else() function of dplyr. Alternatively you could create a custom step function to do this and add the step function to your recipe, but that is beyond the scope of this case study.

We need to create a new recipe to move forward, as the levels of our variables are established then. We would also potentially have this issue for state and county. So let’s also do a similar thing for state. The county variables appears to get dropped due to either correlation or near zero variance. It is likely due to near zero variance becuase this is the more granular of these geographic categorical variables and likely sparse.

<Analysis/Assess/Total>
<584/292/876>

Now let’s retrain our training data and try baking our test data:

oper 1 step dummy [training] 
oper 2 step corr [training] 
oper 3 step nzv [training] 
The retained training set is ~ 0.26 Mb  in memory.

Rows: 584
Columns: 37
$ id                          <fct> 1003.001, 1027.0001, 1033.1002, 1055.001,…
$ value                       <dbl> 9.597647, 10.800000, 11.212174, 12.375394…
$ fips                        <fct> 1003, 1027, 1033, 1055, 1069, 1073, 1073,…
$ lat                         <dbl> 30.49800, 33.28126, 34.75878, 33.99375, 3…
$ lon                         <dbl> -87.88141, -85.80218, -87.65056, -85.9910…
$ CMAQ                        <dbl> 8.098836, 9.766208, 9.402679, 9.241744, 9…
$ zcta_area                   <dbl> 190980522, 374132430, 16716984, 154069359…
$ zcta_pop                    <dbl> 27829, 5103, 9042, 20045, 30217, 9010, 16…
$ imp_a500                    <dbl> 0.01730104, 1.96972318, 19.17301038, 16.4…
$ imp_a15000                  <dbl> 1.4386207, 0.3359198, 5.2472094, 5.161210…
$ county_area                 <dbl> 4117521611, 1564252280, 1534877333, 13856…
$ county_pop                  <dbl> 182265, 13932, 54428, 104430, 101547, 658…
$ log_dist_to_prisec          <dbl> 4.648181, 7.219907, 5.760131, 5.261457, 7…
$ log_pri_length_5000         <dbl> 8.517193, 8.517193, 8.517193, 9.066563, 8…
$ log_pri_length_25000        <dbl> 11.32735, 10.12663, 10.15769, 12.01356, 1…
$ log_prisec_length_500       <dbl> 7.295356, 6.214608, 8.611945, 8.740680, 6…
$ log_prisec_length_1000      <dbl> 8.195119, 7.600902, 9.735569, 9.627898, 7…
$ log_prisec_length_5000      <dbl> 10.815042, 10.170878, 11.770407, 11.72888…
$ log_prisec_length_10000     <dbl> 11.886803, 11.405543, 12.840663, 12.76827…
$ log_prisec_length_25000     <dbl> 13.41395, 12.79980, 13.79973, 13.70026, 1…
$ log_nei_2008_pm10_sum_15000 <dbl> 2.26783411, 3.31111648, 6.70127741, 4.462…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.628728, 3.311116, 7.148858, 4.678311, 3…
$ popdens_county              <dbl> 44.265706, 8.906492, 35.460814, 75.367038…
$ popdens_zcta                <dbl> 145.7164307, 13.6395554, 540.8870404, 130…
$ nohs                        <dbl> 3.3, 11.6, 7.3, 4.3, 5.8, 7.1, 2.7, 11.1,…
$ somehs                      <dbl> 4.9, 19.1, 15.8, 13.3, 11.6, 17.1, 6.6, 1…
$ hs                          <dbl> 25.1, 33.9, 30.6, 27.8, 29.8, 37.2, 30.7,…
$ somecollege                 <dbl> 19.7, 18.8, 20.9, 29.2, 21.4, 23.5, 25.7,…
$ associate                   <dbl> 8.2, 8.0, 7.6, 10.1, 7.9, 7.3, 8.0, 4.1, …
$ bachelor                    <dbl> 25.3, 5.5, 12.7, 10.0, 13.7, 5.9, 17.6, 7…
$ grad                        <dbl> 13.5, 3.1, 5.1, 5.4, 9.8, 2.0, 8.7, 2.9, …
$ pov                         <dbl> 6.1, 19.5, 19.0, 8.8, 15.6, 25.5, 7.3, 8.…
$ hs_orless                   <dbl> 33.3, 64.6, 53.7, 45.4, 47.2, 61.4, 40.0,…
$ urc2013                     <dbl> 4, 6, 4, 4, 4, 1, 1, 1, 1, 1, 2, 3, 3, 3,…
$ aod                         <dbl> 37.363636, 34.818182, 36.000000, 43.41666…
$ state_Not.California        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ city_Not.in.a.city          <dbl> 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,…

Notice, it looks like we gained the log_prisec_length_25000 back with this recipe using the data with our changes to state and city.

Rows: 292
Columns: 37
$ id                          <fct> 1049.1003, 1073.101, 1073.2006, 1089.0014…
$ value                       <dbl> 11.659091, 13.114545, 12.228125, 12.23294…
$ fips                        <fct> 1049, 1073, 1073, 1089, 1103, 1121, 4013,…
$ lat                         <dbl> 34.28763, 33.54528, 33.38639, 34.68767, 3…
$ lon                         <dbl> -85.96830, -86.54917, -86.81667, -86.5863…
$ CMAQ                        <dbl> 8.534772, 9.303766, 10.235612, 9.343611, …
$ zcta_area                   <dbl> 203836235, 148994881, 56063756, 46963946,…
$ zcta_pop                    <dbl> 8300, 14212, 32390, 21297, 30545, 7713, 5…
$ imp_a500                    <dbl> 5.78200692, 0.06055363, 42.42820069, 23.2…
$ imp_a15000                  <dbl> 0.9730444, 2.9956557, 12.7487614, 10.3555…
$ county_area                 <dbl> 2012662359, 2878192209, 2878192209, 20761…
$ county_pop                  <dbl> 71109, 658466, 658466, 334811, 119490, 82…
$ log_dist_to_prisec          <dbl> 3.721489, 7.301545, 4.721755, 4.659519, 6…
$ log_pri_length_5000         <dbl> 8.517193, 9.683336, 10.737240, 8.517193, …
$ log_pri_length_25000        <dbl> 11.90959, 12.53777, 12.99669, 11.47391, 1…
$ log_prisec_length_500       <dbl> 7.310155, 6.214608, 7.528913, 8.760549, 6…
$ log_prisec_length_1000      <dbl> 8.585843, 7.600902, 9.342290, 9.543183, 8…
$ log_prisec_length_5000      <dbl> 10.214200, 11.262645, 11.713190, 11.48606…
$ log_prisec_length_10000     <dbl> 11.50894, 12.14101, 12.53899, 12.68440, 1…
$ log_prisec_length_25000     <dbl> 13.55979, 14.08915, 14.27363, 13.87170, 1…
$ log_nei_2008_pm10_sum_15000 <dbl> 3.3500444, 6.6241114, 5.8268686, 3.861625…
$ log_nei_2008_pm10_sum_25000 <dbl> 5.1719202, 7.5490587, 8.8205542, 5.219092…
$ popdens_county              <dbl> 35.330814, 228.777633, 228.777633, 161.26…
$ popdens_zcta                <dbl> 40.718962, 95.385827, 577.735106, 453.475…
$ nohs                        <dbl> 14.3, 7.2, 0.8, 1.2, 4.8, 16.7, 19.1, 6.4…
$ somehs                      <dbl> 16.7, 12.2, 2.6, 3.1, 7.8, 33.3, 15.6, 9.…
$ hs                          <dbl> 35.0, 32.2, 12.9, 15.1, 28.7, 37.5, 26.5,…
$ somecollege                 <dbl> 14.9, 19.0, 17.9, 20.5, 25.0, 12.5, 18.0,…
$ associate                   <dbl> 5.5, 6.8, 5.2, 6.5, 7.5, 0.0, 6.0, 8.8, 3…
$ bachelor                    <dbl> 7.9, 14.8, 35.5, 30.4, 18.2, 0.0, 10.6, 1…
$ grad                        <dbl> 5.8, 7.7, 25.2, 23.3, 8.0, 0.0, 4.1, 5.7,…
$ pov                         <dbl> 13.8, 10.5, 2.1, 5.2, 8.3, 18.8, 21.4, 14…
$ hs_orless                   <dbl> 66.0, 51.6, 16.3, 19.4, 41.3, 87.5, 61.2,…
$ urc2013                     <dbl> 6, 1, 1, 3, 4, 5, 1, 2, 5, 4, 4, 6, 6, 1,…
$ aod                         <dbl> 33.08333, 42.45455, 44.25000, 42.41667, 4…
$ state_Not.California        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,…
$ city_Not.in.a.city          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…

Great now we no longer have NA values! :)

Note: if you use the skip option for some of the preprocessing steps, be careful. juice() will show all of the results ignoring skip = TRUE. bake() will not necessarily conduct these steps on the new data.

Specifying the Model

So far we have used rsample to split the data and recipes to assign variable and to specify and prep our preprocessing (as well as to optionally extract the preprocessed data).

We will now use the parsnip package (which is similar to the previous caret package - and hence why it is named after the vegetable) to specify our model.

There are four aspects to define about our model:
1) the type of model (using specific funtions in parsnip like rand_forest(), logistic_reg() etc.)
2) the mode of learning - classification or regression (using the set_mode() function)
3) the package or engine that we will use to implement the type of model selected (using the set_engine() function)
4) any arguments necessary for the model/package selected (using the set_args()function - for example the mtry = argument for random forest which is the number of variables to be used as options for splitting at each tree node)

We are going to start our analysis with a linear regression but we will demonstrate how we can try different models.

The first thing we do is define what type of model we would like to use. See here for modeling [options]in parsnip.

Linear Regression Model Specification (regression)

OK. So far all we have told parsnip is we want to use a linear regression… Let’s tell parsnip more about what we want.

We would like to use the ordinary least squares method to fit our linear regression. So we will tell parsnip that we want to use the lm package to implement our linear regression (there are many options actually- such as rstan glmnet, keras, and sparklyr). We will do so by using the set_engine() function of the parsnip package.

Linear Regression Model Specification (regression)

Computational engine: lm 

In some cases some packages can do either classification or prediction, so it is a good idea to specify which mode you intend to perform. You can do this with the set_mode() function of the parsnip package, by using either set_mode("classification") or set_mode("regression").

Linear Regression Model Specification (regression)

Computational engine: lm 

Fitting the Model: two ways - workflows and parsnip

To fit our model we can use the parsnip package and then assess our fit using the yardstick package.

However a newer package called workflows allows us to keep track of both our preprocessing steps and our model specification. It also allows us to implement fancier optimizations in an automated way and it is currently being developed to also handle post-processing operations, so it is good to learn about it!

So we will now create a workflow with the recipe (our preprocessing specifications) that we made and the model that we just specified.

First we use the workflow() function of the workflows package to create a workflow.

Then we add our recipe with the add_recipe() function and we add our model with the add_model() function of the workflows package.

Note: We do not need to actually prep our recipe before using workflows!

══ Workflow ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()

── Preprocessor ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3 Recipe Steps

● step_dummy()
● step_corr()
● step_nzv()

── Model ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Linear Regression Model Specification (regression)

Computational engine: lm 

Ah, nice. Notice how it tells us about both our preprocessing steps and our model specifications.

Now we can prepare the recipe (estimate the parameters) and fit the model to our training data all at once. Printing the output we can see the coefficients of the model.

══ Workflow [trained] ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()

── Preprocessor ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3 Recipe Steps

● step_dummy()
● step_corr()
● step_nzv()

── Model ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Call:
stats::lm(formula = formula, data = data)

Coefficients:
                (Intercept)                          lat  
                  2.265e+02                    3.639e-02  
                        lon                         CMAQ  
                  2.579e-02                    2.847e-01  
                  zcta_area                     zcta_pop  
                  3.638e-10                    7.880e-06  
                   imp_a500                   imp_a15000  
                  7.453e-03                   -1.140e-03  
                county_area                   county_pop  
                 -2.116e-11                   -2.156e-07  
         log_dist_to_prisec          log_pri_length_5000  
                  4.064e-02                   -1.842e-01  
       log_pri_length_25000        log_prisec_length_500  
                  7.936e-03                    2.572e-01  
     log_prisec_length_1000       log_prisec_length_5000  
                 -2.288e-02                    4.743e-01  
    log_prisec_length_10000      log_prisec_length_25000  
                 -1.410e-01                    4.658e-01  
log_nei_2008_pm10_sum_15000  log_nei_2008_pm10_sum_25000  
                  1.088e-01                    5.255e-02  
             popdens_county                 popdens_zcta  
                 -4.114e-05                   -1.816e-05  
                       nohs                       somehs  
                 -2.234e+00                   -2.270e+00  
                         hs                  somecollege  
                 -2.271e+00                   -2.274e+00  
                  associate                     bachelor  
                 -2.272e+00                   -2.277e+00  
                       grad                          pov  
                 -2.284e+00                    7.001e-03  
                  hs_orless                      urc2013  
                         NA                    2.153e-01  
                        aod         state_Not.California  
                  2.555e-02                   -3.445e+00  
         city_Not.in.a.city  
                  3.550e-01  

Otherwise we could have done this without the workflows package. Notice here we will used the processed training data (juiced_train) as opposed to the raw training data that we used with the workflow we created with workflows.

In this case, we actually need to write your model again! Recall that id and fips are ID variables and that values is our outcome of interest (the pm air pollution measure at each monitor).

Looking at model fit with broom

The broom package allows for an easy/tidy way to look at the fitted model:

tidy() grabs the coefficients from the model
glance() summarizes the model fit and gives us an idea about how well the model might perform augment() gives a 150 row observation level summary of the data and fit

These broom functions currently only work with parsnip objects not raw workflows objects. To use the tidy() function with workflows we need to first use the pull_workflow_fit() function.

# A tibble: 34 x 5
   term                     estimate    std.error statistic  p.value
   <chr>                       <dbl>        <dbl>     <dbl>    <dbl>
 1 state_Not.California -3.44        0.436            -7.91 1.45e-14
 2 CMAQ                  0.285       0.0430            6.61 8.90e-11
 3 aod                   0.0256      0.00575           4.45 1.06e- 5
 4 lon                   0.0258      0.00998           2.58 1.00e- 2
 5 county_pop           -0.000000216 0.0000000934     -2.31 2.14e- 2
 6 urc2013               0.215       0.101             2.13 3.35e- 2
 7 grad                 -2.28        1.20             -1.90 5.78e- 2
 8 bachelor             -2.28        1.20             -1.90 5.84e- 2
 9 somecollege          -2.27        1.20             -1.89 5.87e- 2
10 associate            -2.27        1.20             -1.89 5.90e- 2
# … with 24 more rows
# A tibble: 1 x 11
  r.squared adj.r.squared sigma statistic  p.value    df logLik   AIC   BIC
      <dbl>         <dbl> <dbl>     <dbl>    <dbl> <int>  <dbl> <dbl> <dbl>
1     0.433         0.399  2.08      12.7 1.02e-48    34 -1240. 2550. 2703.
# … with 2 more variables: deviance <dbl>, df.residual <int>
# A tibble: 584 x 42
   value   lat   lon  CMAQ zcta_area zcta_pop imp_a500 imp_a15000 county_area
   <dbl> <dbl> <dbl> <dbl>     <dbl>    <dbl>    <dbl>      <dbl>       <dbl>
 1  9.60  30.5 -87.9  8.10 190980522    27829   0.0173      1.44   4117521611
 2 10.8   33.3 -85.8  9.77 374132430     5103   1.97        0.336  1564252280
 3 11.2   34.8 -87.7  9.40  16716984     9042  19.2         5.25   1534877333
 4 12.4   34.0 -86.0  9.24 154069359    20045  16.5         5.16   1385618994
 5 10.5   31.2 -85.4  9.12 162685124    30217  19.1         4.74   1501737720
 6 15.6   33.6 -86.8 10.2   26929603     9010  41.8        17.5    2878192209
 7 12.4   33.3 -87.0 10.2  166239542    16140   1.70        4.30   2878192209
 8 11.1   33.5 -87.3  8.16 385566685     3699   0           0.162  3423328940
 9 14.6   33.5 -86.9 10.2   10636977    11458  43.6        15.6    2878192209
10 12.0   33.7 -86.7  9.30 150661846    21725   1.48        4.25   2878192209
# … with 574 more rows, and 33 more variables: county_pop <dbl>,
#   log_dist_to_prisec <dbl>, log_pri_length_5000 <dbl>,
#   log_pri_length_25000 <dbl>, log_prisec_length_500 <dbl>,
#   log_prisec_length_1000 <dbl>, log_prisec_length_5000 <dbl>,
#   log_prisec_length_10000 <dbl>, log_prisec_length_25000 <dbl>,
#   log_nei_2008_pm10_sum_15000 <dbl>, log_nei_2008_pm10_sum_25000 <dbl>,
#   popdens_county <dbl>, popdens_zcta <dbl>, nohs <dbl>, somehs <dbl>,
#   hs <dbl>, somecollege <dbl>, associate <dbl>, bachelor <dbl>, grad <dbl>,
#   pov <dbl>, hs_orless <dbl>, urc2013 <dbl>, aod <dbl>,
#   state_Not.California <dbl>, city_Not.in.a.city <dbl>, .fitted <dbl>,
#   .se.fit <dbl>, .resid <dbl>, .hat <dbl>, .sigma <dbl>, .cooksd <dbl>,
#   .std.resid <dbl>
[1] TRUE

OK, so we have fit our model on our training data, which means we have created a model to predict values of air pollution based on the predictors that we have included. Yay!

We can get a sense of the variable importance using the vip() function of the vip package.

Let’s take a look at the top 10 contributing variables:

Model Performance

Let’s take a look at how well our model fit our training data:

        1         2         3         4         5         6         7         8 
 9.461664 10.429189 11.795351 11.139746 10.863402 11.091857 10.073411  8.189021 
        9        10        11        12        13        14        15        16 
11.517888 10.618367  9.557499 10.218323  9.865608 11.011802 12.139040 10.202025 
       17        18        19        20        21        22        23        24 
10.517763  9.548571  6.837646  7.472453  7.018230 10.126795 10.133236  9.582305 
       25        26        27        28        29        30        31        32 
 9.702583  6.862089  7.005665  8.477788  8.892971  9.244236 11.417965 10.464960 
       33        34        35        36        37        38        39        40 
 9.652665  9.943594 11.435373 10.090206 10.304301 11.188076 11.193689 10.767763 
       41        42        43        44        45        46        47        48 
10.722817  9.949272 10.521367  9.976252 11.955651 11.674338 13.902418 13.582229 
       49        50        51        52        53        54        55        56 
14.713622 11.219505 11.320158 15.169192  8.682176 13.357477 11.166948 14.162085 
       57        58        59        60        61        62        63        64 
13.128209 10.867659 12.557864 11.404720 14.609938 10.003168 11.917995  9.886651 
       65        66        67        68        69        70        71        72 
11.161819 12.326977 12.722475 11.439546 11.380029 14.615337 10.945608 12.754387 
       73        74        75        76        77        78        79        80 
11.464256 13.050833 11.162822 14.092701 10.025276 12.181293 12.573405 12.850499 
       81        82        83        84        85        86        87        88 
12.523434 13.398889  8.867379 12.250365 15.385913 10.726820 12.320977 12.282267 
       89        90        91        92        93        94        95        96 
10.903510 13.504498 10.678776 11.367829 14.267153 11.959658 14.860756 12.245437 
       97        98        99       100       101       102       103       104 
11.009600 12.303933 10.376440  9.254959  8.458964 10.797185  9.615059  8.319766 
      105       106       107       108       109       110       111       112 
 6.584026  8.480982  7.889599  8.520672  9.507922  9.460726 12.291791 10.315367 
      113       114       115       116       117       118       119       120 
 9.899759 10.270368  9.531778  8.476645 10.506159 10.457589 11.548915 12.211470 
      121       122       123       124       125       126       127       128 
12.754005 11.760960 11.527420 13.358326 12.970527 11.450112  9.662891  8.860986 
      129       130       131       132       133       134       135       136 
 9.630399  9.107099  9.336214 10.377070 10.913731 10.012413 11.655663  9.197286 
      137       138       139       140       141       142       143       144 
 9.895577  8.992575 11.062182 10.928292 11.173204  9.290713  9.462250  9.815623 
      145       146       147       148       149       150       151       152 
10.567132 10.041853 12.438193 12.837043 11.155833 11.068409 11.568628 10.646408 
      153       154       155       156       157       158       159       160 
11.120305 10.674787 11.342914 11.600863  9.872202 12.933636 12.330424 10.674265 
      161       162       163       164       165       166       167       168 
10.692636  8.591527  8.483559  8.277465  9.219110  7.999004  7.325638 10.668789 
      169       170       171       172       173       174       175       176 
10.442171 13.278721 12.987068 12.609307 12.139812 13.311952  9.560588 11.231509 
      177       178       179       180       181       182       183       184 
 9.614572 11.388626 11.027188 10.701644 10.408321 11.281993 11.952935 12.314095 
      185       186       187       188       189       190       191       192 
11.343060 12.301626 11.670473 11.538465 11.450721 12.583606  9.908812 11.957886 
      193       194       195       196       197       198       199       200 
12.476480 11.356784 11.408551 11.386678 11.371265 11.869010 11.386166 10.716040 
      201       202       203       204       205       206       207       208 
13.547182 13.430563 14.488555 13.382721 11.721882 13.138956 11.617227 12.919960 
      209       210       211       212       213       214       215       216 
11.610622 11.234236 12.441517 11.565354 11.775386 11.870380 11.612343 10.513345 
      217       218       219       220       221       222       223       224 
11.332066 11.356102  8.622875  9.342198 10.532866 11.075768 10.814015  9.148910 
      225       226       227       228       229       230       231       232 
10.259892  8.851762 10.150785 11.702025 11.247041  9.448320  8.715848 10.465157 
      233       234       235       236       237       238       239       240 
 9.063738  8.512195  9.359375  8.694810 12.250888 10.950829 11.172992 11.113227 
      241       242       243       244       245       246       247       248 
12.539577 11.130235 11.520853 13.270299 12.445247 11.641586 11.084325 11.681937 
      249       250       251       252       253       254       255       256 
12.903454 11.675062 11.868743 11.887980 12.142425 11.125280 10.206552 13.083514 
      257       258       259       260       261       262       263       264 
12.553520 13.232212 11.590232 11.070216 11.072157 11.721994 10.595329 14.228343 
      265       266       267       268       269       270       271       272 
13.651194 10.111261 12.956021 13.017707 10.712920 13.277355 11.914351 11.578296 
      273       274       275       276       277       278       279       280 
12.601671 13.808564 13.137512 11.706520 11.860984  9.304048 10.849169 10.160520 
      281       282       283       284       285       286       287       288 
10.957044 10.853018 11.595010 11.208375 11.222850 10.646720 10.875621 10.875865 
      289       290       291       292       293       294       295       296 
12.139867  9.344793  7.829627  7.419011 10.276070  8.595271 11.984527 10.025066 
      297       298       299       300       301       302       303       304 
11.658014 10.844432 13.668283 13.877468 11.842378 12.609642 12.315624  7.550419 
      305       306       307       308       309       310       311       312 
10.543817 10.140751 10.640678  9.986984 10.672587 12.808831  8.608027  8.762374 
      313       314       315       316       317       318       319       320 
 9.800301  8.990358 10.913175 10.986842  9.848777 10.184492 10.730669  8.925857 
      321       322       323       324       325       326       327       328 
 9.810568 11.051639 12.301691 13.244583 12.143078  7.420562  7.181852  8.115063 
      329       330       331       332       333       334       335       336 
 7.557550  7.598764  7.963658  6.033631  8.174960  7.009403  6.547324  6.721532 
      337       338       339       340       341       342       343       344 
10.495751 10.127753  8.447220  8.955561  9.111061  6.229223  7.721007  8.702168 
      345       346       347       348       349       350       351       352 
 9.796073 10.735891 10.674564  9.993831  9.387035 11.404517 12.718761 12.490109 
      353       354       355       356       357       358       359       360 
12.490096 12.868844 14.159822 11.862943 12.490662 12.814318 13.292919 11.342429 
      361       362       363       364       365       366       367       368 
11.790078 13.543314 12.475061 11.439849  9.501710  9.938767  8.662747  9.361561 
      369       370       371       372       373       374       375       376 
 7.333849  7.619211  8.424590  8.182639  8.244907 11.421981 12.598988 12.766756 
      377       378       379       380       381       382       383       384 
10.004449 11.846089 11.484665  8.502844  9.985085 14.093239 11.022401 10.343070 
      385       386       387       388       389       390       391       392 
10.870999 11.580658  8.164514  9.816118 13.810603 10.765304 10.679352  8.943409 
      393       394       395       396       397       398       399       400 
11.394455 10.988983 11.911609 11.169336 10.926602 11.106484 10.975384 10.570735 
      401       402       403       404       405       406       407       408 
10.800890 11.216585 11.003865 12.058352  8.979768 10.417642 10.840787  8.564483 
      409       410       411       412       413       414       415       416 
10.468748  7.722598  9.024726  8.627277  8.712337 12.306801 11.259834 12.135047 
      417       418       419       420       421       422       423       424 
12.909887 13.900961 14.613681 14.659878 14.516719 11.834130 12.835139 11.524950 
      425       426       427       428       429       430       431       432 
11.205715 12.690535 12.366608 12.257143 11.554346 11.571765 10.268979 13.831592 
      433       434       435       436       437       438       439       440 
12.488541 12.274712 11.792411 12.159000 11.286455 12.398744 12.720250 11.839790 
      441       442       443       444       445       446       447       448 
12.042530 10.616712 11.592961  8.680015 10.002905 10.098331  9.303447  8.025186 
      449       450       451       452       453       454       455       456 
 8.160942  7.701500  7.573096  6.440509  8.413890  7.573817  8.654901  6.527707 
      457       458       459       460       461       462       463       464 
 9.316892  9.239795  7.577079  7.234717  7.744910 10.396131 11.349235 10.531142 
      465       466       467       468       469       470       471       472 
11.286622 11.324207 11.904075 11.984744 11.254344 10.087881 12.491983 12.727448 
      473       474       475       476       477       478       479       480 
11.023725  9.843285 12.532214 10.909423 11.556938 11.246725 13.678810 10.995575 
      481       482       483       484       485       486       487       488 
13.406587 12.947436 11.536939 10.519024 10.614975 10.691272 12.606993 11.966835 
      489       490       491       492       493       494       495       496 
11.220192  9.923407 11.012463  9.424555 10.077508 10.808538 10.687251 11.438914 
      497       498       499       500       501       502       503       504 
12.408781 10.434905  8.167398  7.968639  7.492743  7.971856  7.794390  8.631370 
      505       506       507       508       509       510       511       512 
 7.279254  7.833077 10.330354 10.925965 10.909906  8.503572 12.117403 10.667929 
      513       514       515       516       517       518       519       520 
10.129061 10.550327  9.918316 11.522068 11.296737 11.553654 10.476981 11.941762 
      521       522       523       524       525       526       527       528 
10.578826 11.080807  9.211676 10.323909  9.621231  8.335038  8.188955  9.082492 
      529       530       531       532       533       534       535       536 
 8.075345  9.420944  8.314572  9.247623  8.538591  9.859266  7.647124  9.902155 
      537       538       539       540       541       542       543       544 
 8.546524  9.920558 11.604894 11.918312 12.416602 10.819737 11.466657 11.179962 
      545       546       547       548       549       550       551       552 
11.665760  9.817793 14.172912 10.192052 11.355575 13.772991  8.847900  7.970775 
      553       554       555       556       557       558       559       560 
 7.460673  8.349559  8.135941 11.447727 11.618942 11.936942 10.918982 11.041702 
      561       562       563       564       565       566       567       568 
 9.949377 11.499303 10.904615 11.689807 11.747444 11.380585 11.009586 10.850464 
      569       570       571       572       573       574       575       576 
11.602371 11.551376 12.485626 11.270907 10.170153 10.619552  9.784335  8.033726 
      577       578       579       580       581       582       583       584 
10.810373  8.993775  5.743434  8.959191  8.089329  6.061699  8.339742  6.931294 
# A tibble: 584 x 8
   value .fitted .se.fit .resid   .hat .sigma    .cooksd .std.resid
   <dbl>   <dbl>   <dbl>  <dbl>  <dbl>  <dbl>      <dbl>      <dbl>
 1  9.60    9.46   0.375  0.136 0.0324   2.09 0.00000433     0.0663
 2 10.8    10.4    0.383  0.371 0.0338   2.09 0.0000338      0.181 
 3 11.2    11.8    0.404 -0.583 0.0376   2.09 0.0000936     -0.285 
 4 12.4    11.1    0.388  1.24  0.0346   2.08 0.000384       0.604 
 5 10.5    10.9    0.426 -0.355 0.0418   2.09 0.0000388     -0.174 
 6 15.6    11.1    0.379  4.50  0.0332   2.08 0.00486        2.20  
 7 12.4    10.1    0.459  2.33  0.0485   2.08 0.00196        1.14  
 8 11.1     8.19   0.777  2.91  0.139    2.08 0.0108         1.51  
 9 14.6    11.5    0.378  3.03  0.0329   2.08 0.00219        1.48  
10 12.0    10.6    0.452  1.33  0.0470   2.08 0.000623       0.656 
# … with 574 more rows
        1         2         3         4         5         6         7         8 
 9.461664 10.429189 11.795351 11.139746 10.863402 11.091857 10.073411  8.189021 
        9        10        11        12        13        14        15        16 
11.517888 10.618367  9.557499 10.218323  9.865608 11.011802 12.139040 10.202025 
       17        18        19        20        21        22        23        24 
10.517763  9.548571  6.837646  7.472453  7.018230 10.126795 10.133236  9.582305 
       25        26        27        28        29        30        31        32 
 9.702583  6.862089  7.005665  8.477788  8.892971  9.244236 11.417965 10.464960 
       33        34        35        36        37        38        39        40 
 9.652665  9.943594 11.435373 10.090206 10.304301 11.188076 11.193689 10.767763 
       41        42        43        44        45        46        47        48 
10.722817  9.949272 10.521367  9.976252 11.955651 11.674338 13.902418 13.582229 
       49        50        51        52        53        54        55        56 
14.713622 11.219505 11.320158 15.169192  8.682176 13.357477 11.166948 14.162085 
       57        58        59        60        61        62        63        64 
13.128209 10.867659 12.557864 11.404720 14.609938 10.003168 11.917995  9.886651 
       65        66        67        68        69        70        71        72 
11.161819 12.326977 12.722475 11.439546 11.380029 14.615337 10.945608 12.754387 
       73        74        75        76        77        78        79        80 
11.464256 13.050833 11.162822 14.092701 10.025276 12.181293 12.573405 12.850499 
       81        82        83        84        85        86        87        88 
12.523434 13.398889  8.867379 12.250365 15.385913 10.726820 12.320977 12.282267 
       89        90        91        92        93        94        95        96 
10.903510 13.504498 10.678776 11.367829 14.267153 11.959658 14.860756 12.245437 
       97        98        99       100       101       102       103       104 
11.009600 12.303933 10.376440  9.254959  8.458964 10.797185  9.615059  8.319766 
      105       106       107       108       109       110       111       112 
 6.584026  8.480982  7.889599  8.520672  9.507922  9.460726 12.291791 10.315367 
      113       114       115       116       117       118       119       120 
 9.899759 10.270368  9.531778  8.476645 10.506159 10.457589 11.548915 12.211470 
      121       122       123       124       125       126       127       128 
12.754005 11.760960 11.527420 13.358326 12.970527 11.450112  9.662891  8.860986 
      129       130       131       132       133       134       135       136 
 9.630399  9.107099  9.336214 10.377070 10.913731 10.012413 11.655663  9.197286 
      137       138       139       140       141       142       143       144 
 9.895577  8.992575 11.062182 10.928292 11.173204  9.290713  9.462250  9.815623 
      145       146       147       148       149       150       151       152 
10.567132 10.041853 12.438193 12.837043 11.155833 11.068409 11.568628 10.646408 
      153       154       155       156       157       158       159       160 
11.120305 10.674787 11.342914 11.600863  9.872202 12.933636 12.330424 10.674265 
      161       162       163       164       165       166       167       168 
10.692636  8.591527  8.483559  8.277465  9.219110  7.999004  7.325638 10.668789 
      169       170       171       172       173       174       175       176 
10.442171 13.278721 12.987068 12.609307 12.139812 13.311952  9.560588 11.231509 
      177       178       179       180       181       182       183       184 
 9.614572 11.388626 11.027188 10.701644 10.408321 11.281993 11.952935 12.314095 
      185       186       187       188       189       190       191       192 
11.343060 12.301626 11.670473 11.538465 11.450721 12.583606  9.908812 11.957886 
      193       194       195       196       197       198       199       200 
12.476480 11.356784 11.408551 11.386678 11.371265 11.869010 11.386166 10.716040 
      201       202       203       204       205       206       207       208 
13.547182 13.430563 14.488555 13.382721 11.721882 13.138956 11.617227 12.919960 
      209       210       211       212       213       214       215       216 
11.610622 11.234236 12.441517 11.565354 11.775386 11.870380 11.612343 10.513345 
      217       218       219       220       221       222       223       224 
11.332066 11.356102  8.622875  9.342198 10.532866 11.075768 10.814015  9.148910 
      225       226       227       228       229       230       231       232 
10.259892  8.851762 10.150785 11.702025 11.247041  9.448320  8.715848 10.465157 
      233       234       235       236       237       238       239       240 
 9.063738  8.512195  9.359375  8.694810 12.250888 10.950829 11.172992 11.113227 
      241       242       243       244       245       246       247       248 
12.539577 11.130235 11.520853 13.270299 12.445247 11.641586 11.084325 11.681937 
      249       250       251       252       253       254       255       256 
12.903454 11.675062 11.868743 11.887980 12.142425 11.125280 10.206552 13.083514 
      257       258       259       260       261       262       263       264 
12.553520 13.232212 11.590232 11.070216 11.072157 11.721994 10.595329 14.228343 
      265       266       267       268       269       270       271       272 
13.651194 10.111261 12.956021 13.017707 10.712920 13.277355 11.914351 11.578296 
      273       274       275       276       277       278       279       280 
12.601671 13.808564 13.137512 11.706520 11.860984  9.304048 10.849169 10.160520 
      281       282       283       284       285       286       287       288 
10.957044 10.853018 11.595010 11.208375 11.222850 10.646720 10.875621 10.875865 
      289       290       291       292       293       294       295       296 
12.139867  9.344793  7.829627  7.419011 10.276070  8.595271 11.984527 10.025066 
      297       298       299       300       301       302       303       304 
11.658014 10.844432 13.668283 13.877468 11.842378 12.609642 12.315624  7.550419 
      305       306       307       308       309       310       311       312 
10.543817 10.140751 10.640678  9.986984 10.672587 12.808831  8.608027  8.762374 
      313       314       315       316       317       318       319       320 
 9.800301  8.990358 10.913175 10.986842  9.848777 10.184492 10.730669  8.925857 
      321       322       323       324       325       326       327       328 
 9.810568 11.051639 12.301691 13.244583 12.143078  7.420562  7.181852  8.115063 
      329       330       331       332       333       334       335       336 
 7.557550  7.598764  7.963658  6.033631  8.174960  7.009403  6.547324  6.721532 
      337       338       339       340       341       342       343       344 
10.495751 10.127753  8.447220  8.955561  9.111061  6.229223  7.721007  8.702168 
      345       346       347       348       349       350       351       352 
 9.796073 10.735891 10.674564  9.993831  9.387035 11.404517 12.718761 12.490109 
      353       354       355       356       357       358       359       360 
12.490096 12.868844 14.159822 11.862943 12.490662 12.814318 13.292919 11.342429 
      361       362       363       364       365       366       367       368 
11.790078 13.543314 12.475061 11.439849  9.501710  9.938767  8.662747  9.361561 
      369       370       371       372       373       374       375       376 
 7.333849  7.619211  8.424590  8.182639  8.244907 11.421981 12.598988 12.766756 
      377       378       379       380       381       382       383       384 
10.004449 11.846089 11.484665  8.502844  9.985085 14.093239 11.022401 10.343070 
      385       386       387       388       389       390       391       392 
10.870999 11.580658  8.164514  9.816118 13.810603 10.765304 10.679352  8.943409 
      393       394       395       396       397       398       399       400 
11.394455 10.988983 11.911609 11.169336 10.926602 11.106484 10.975384 10.570735 
      401       402       403       404       405       406       407       408 
10.800890 11.216585 11.003865 12.058352  8.979768 10.417642 10.840787  8.564483 
      409       410       411       412       413       414       415       416 
10.468748  7.722598  9.024726  8.627277  8.712337 12.306801 11.259834 12.135047 
      417       418       419       420       421       422       423       424 
12.909887 13.900961 14.613681 14.659878 14.516719 11.834130 12.835139 11.524950 
      425       426       427       428       429       430       431       432 
11.205715 12.690535 12.366608 12.257143 11.554346 11.571765 10.268979 13.831592 
      433       434       435       436       437       438       439       440 
12.488541 12.274712 11.792411 12.159000 11.286455 12.398744 12.720250 11.839790 
      441       442       443       444       445       446       447       448 
12.042530 10.616712 11.592961  8.680015 10.002905 10.098331  9.303447  8.025186 
      449       450       451       452       453       454       455       456 
 8.160942  7.701500  7.573096  6.440509  8.413890  7.573817  8.654901  6.527707 
      457       458       459       460       461       462       463       464 
 9.316892  9.239795  7.577079  7.234717  7.744910 10.396131 11.349235 10.531142 
      465       466       467       468       469       470       471       472 
11.286622 11.324207 11.904075 11.984744 11.254344 10.087881 12.491983 12.727448 
      473       474       475       476       477       478       479       480 
11.023725  9.843285 12.532214 10.909423 11.556938 11.246725 13.678810 10.995575 
      481       482       483       484       485       486       487       488 
13.406587 12.947436 11.536939 10.519024 10.614975 10.691272 12.606993 11.966835 
      489       490       491       492       493       494       495       496 
11.220192  9.923407 11.012463  9.424555 10.077508 10.808538 10.687251 11.438914 
      497       498       499       500       501       502       503       504 
12.408781 10.434905  8.167398  7.968639  7.492743  7.971856  7.794390  8.631370 
      505       506       507       508       509       510       511       512 
 7.279254  7.833077 10.330354 10.925965 10.909906  8.503572 12.117403 10.667929 
      513       514       515       516       517       518       519       520 
10.129061 10.550327  9.918316 11.522068 11.296737 11.553654 10.476981 11.941762 
      521       522       523       524       525       526       527       528 
10.578826 11.080807  9.211676 10.323909  9.621231  8.335038  8.188955  9.082492 
      529       530       531       532       533       534       535       536 
 8.075345  9.420944  8.314572  9.247623  8.538591  9.859266  7.647124  9.902155 
      537       538       539       540       541       542       543       544 
 8.546524  9.920558 11.604894 11.918312 12.416602 10.819737 11.466657 11.179962 
      545       546       547       548       549       550       551       552 
11.665760  9.817793 14.172912 10.192052 11.355575 13.772991  8.847900  7.970775 
      553       554       555       556       557       558       559       560 
 7.460673  8.349559  8.135941 11.447727 11.618942 11.936942 10.918982 11.041702 
      561       562       563       564       565       566       567       568 
 9.949377 11.499303 10.904615 11.689807 11.747444 11.380585 11.009586 10.850464 
      569       570       571       572       573       574       575       576 
11.602371 11.551376 12.485626 11.270907 10.170153 10.619552  9.784335  8.033726 
      577       578       579       580       581       582       583       584 
10.810373  8.993775  5.743434  8.959191  8.089329  6.061699  8.339742  6.931294 
# A tibble: 584 x 8
   value .fitted .se.fit .resid   .hat .sigma    .cooksd .std.resid
   <dbl>   <dbl>   <dbl>  <dbl>  <dbl>  <dbl>      <dbl>      <dbl>
 1  9.60    9.46   0.375  0.136 0.0324   2.09 0.00000433     0.0663
 2 10.8    10.4    0.383  0.371 0.0338   2.09 0.0000338      0.181 
 3 11.2    11.8    0.404 -0.583 0.0376   2.09 0.0000936     -0.285 
 4 12.4    11.1    0.388  1.24  0.0346   2.08 0.000384       0.604 
 5 10.5    10.9    0.426 -0.355 0.0418   2.09 0.0000388     -0.174 
 6 15.6    11.1    0.379  4.50  0.0332   2.08 0.00486        2.20  
 7 12.4    10.1    0.459  2.33  0.0485   2.08 0.00196        1.14  
 8 11.1     8.19   0.777  2.91  0.139    2.08 0.0108         1.51  
 9 14.6    11.5    0.378  3.03  0.0329   2.08 0.00219        1.48  
10 12.0    10.6    0.452  1.33  0.0470   2.08 0.000623       0.656 
# … with 574 more rows
[1] TRUE

Ok, so our fitted range appears to be smaller than the real values. We could probably do a bit better.

Let’s take a look at how well our model seems to be preforming more formally:

When assessing the performance of a model, the metrics we use depend on if we are preforming a classifcation or prediction also known as regression analysis. In our case we are performing a regression or prediction analysis and the metrics often used are: 1) mean absolute error (mae)
2) R squared error (rsq) This is also known as the coefficient of determiniation which is the squared correlation between truth and estimate
3) root mean squared error (rmse)

We can use the yardstick package to quickly calculate estimates for all of these values using the metrics() function. Alternatively if you only wanted one metric you could use the mae(), rsq(), or rmse() functions respectively. This is helpful to examine with our fitted training set values to see how well our model is performing and if we need to make adjustments.

# A tibble: 3 x 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 rmse    standard       2.02 
2 rsq     standard       0.433
3 mae     standard       1.49 
# A tibble: 1 x 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 mae     standard        1.49

Cross validation sample splitting

We will use the rsample package again in order to further implement what are called cross validation techniques. This is also called resampling or repartioning. Note: we are not actually getting new samples from the underlying distribution so the term resampling is a bit of a misnomer.

Cross validation splits our training data into multiple training data sets to allow for a deeper assessment of the accuracy of the model.

Here is a visualization of the concept for cross validation/resampling/repartitioning from Max Kuhn:

Technically creating our testing and training set out of our original training data is sometimes considered a form of cross validation called the holdout method. As we just learned this can give us a better sense of the accuracy of our data in a more generalizable way.

However, we can do a better job of optimizing our model for accuracy if we also perform another type of cross validation on the newly defined training set that we just created. There are many cross validation methods and most can be easily implemented using rsamples package. We will use a very popular method called either k-fold or v-fold cross validation.

This method involves essentially preforming the hold out method iteratively with the training data.

First the training set is divided into k or v equally sized smaller pieces.

Then the model is trained on the model on k-1 or v-1 subsets of the data iteratively (removing a different v or k untill all possible k-1 or v-1 sets have been evaluated) to get a sense of the performance of the model. This is really useful for fine tuning specific aspects of the model in a process called model tuning.

Here is a visualization of how the folds are created:

Note: People typically ignore spatial dependence with cross validation of air pollution monitoring data in the air pollution field, so we will do the same. However, it might make sense to leave out blocks of monitors rather than random individual monitors to help account for some spatial dependence.

The vfold_cv() function of the rsample package can be used to parse the training data into folds for k-fold/v-fold cross validation.

The v argument specifies the number of folds to create. The repeats argument specifies if any samples should be repeated across folds - defualt is FALSE The strata argument specifies a variable to stratify samples across folds (just like in initial_split()).

Again because these are created at random, we need to use the base set.seed() function in order to obtain the same results each time we knit this document. Generally speaking using 10 folds is good practice, but this depends on the variablity within your data. We are going to use 4 for the sake of expediency.

#  4-fold cross-validation 
# A tibble: 4 x 2
  splits            id   
  <list>            <chr>
1 <split [438/146]> Fold1
2 <split [438/146]> Fold2
3 <split [438/146]> Fold3
4 <split [438/146]> Fold4
NULL
NULL

Once the folds are created they can be used to evaluate peformance by fitting the model to each of the resamples that we created:

We can fit the model to our cross validation folds using the fit_resamples() function of the tune package, by specifying our workflow object and the cross validation fold object we just created. See here for more information.

We can now take a look at various metrics of performance based on the fit of our cross validation “resamples”. To do this we will use the show_best() function of the tune package.

# A tibble: 1 x 5
  .metric .estimator  mean     n std_err
  <chr>   <chr>      <dbl> <int>   <dbl>
1 rmse    standard    2.18     4  0.0763

Tuning

Now let’s try some tuning.

Let’s take a closer look at how the air pollution monitor values vary with the location latitude and longitude.

We can see that there does not appear to be a single linear relationship for either of these predictors. Thus we might want to think about using splines or this(https://towardsdatascience.com/numerical-interpolation-natural-cubic-spline-52c1157b98ac) or just this(https://tidymodels.github.io/tune/articles/getting_started.html) or this(https://www.psych.mcgill.ca/misc/fda/ex-basis-b1.html) to model the relationship in our training data more closely. For example for the latitude plot (left) if we had 2 lines and one breakpoint called a knot around 40, with the first line having a positive slope and the second with a negative slope this would fit the data more similarly to the blue line shown in the figure.

We can tune for the number of knots by using a step function in the recipes package called step_ns() where ns stands for natural splines. In order to tune for the number of knots or degrees of freedom, we can set the deg_free argument to tune(). This is helpful, becuase we aren’t exactly sure how closely we should be following the relationship with the value and our longitude and latitude data in our training data to achieve good accuracy yet keep our model generalizable for other data.

This is when our cross validation methods become really handy. We can test out different values for the deg_free argument and see how our model performance varies across our training folds to try to find the optimal value.

We will update our recipe to add these steps. It is a good idea to do this for individual predictors because you can name each with the tune argument so that you can keep track of it later. We can see what we intend to tune with the parameters() function of the dials package.

See here for more information about implementing this in tidymodels.

Collection of 2 parameters for tuning

     id parameter type object class
 lon df       deg_free    nparam[+]
 lat df       deg_free    nparam[+]

Genearlly you could use the grid_*() functions of the dials package to create the different combinations of degrees of freedom to test for both variables to optimize the model. In our case we can visably see that if we add more than say 4 or 5 degrees of freedom we will likely overfit the data. So instead of using these functions we will create our own grid using the base seq() and expand.grid() functions.

  lon df lat df
1      1      1
2      3      1
3      5      1
4      1      3
5      3      3
6      5      3
7      1      5
8      3      5
9      5      5

Now we will tune this hyper-parameter (degrees of freedom) for both the lat and lon variables using our cross validation folds. To do this we will use the tune_grid() function of the tune package.

#  4-fold cross-validation 
# A tibble: 4 x 4
  splits            id    .metrics          .notes           
  <list>            <chr> <list>            <list>           
1 <split [438/146]> Fold1 <tibble [18 × 5]> <tibble [18 × 1]>
2 <split [438/146]> Fold2 <tibble [18 × 5]> <tibble [18 × 1]>
3 <split [438/146]> Fold3 <tibble [18 × 5]> <tibble [18 × 1]>
4 <split [438/146]> Fold4 <tibble [18 × 5]> <tibble [18 × 1]>
# A tibble: 18 x 7
   `lon df` `lat df` .metric .estimator  mean     n std_err
      <dbl>    <dbl> <chr>   <chr>      <dbl> <int>   <dbl>
 1        1        1 rmse    standard   2.18      4  0.0763
 2        1        1 rsq     standard   0.361     4  0.0486
 3        1        3 rmse    standard   2.13      4  0.0807
 4        1        3 rsq     standard   0.383     4  0.0388
 5        1        5 rmse    standard   2.13      4  0.0812
 6        1        5 rsq     standard   0.386     4  0.0403
 7        3        1 rmse    standard   2.10      4  0.0613
 8        3        1 rsq     standard   0.402     4  0.0296
 9        3        3 rmse    standard   2.05      4  0.0709
10        3        3 rsq     standard   0.428     4  0.0191
11        3        5 rmse    standard   2.03      4  0.0656
12        3        5 rsq     standard   0.439     4  0.0197
13        5        1 rmse    standard   2.11      4  0.0616
14        5        1 rsq     standard   0.397     4  0.0258
15        5        3 rmse    standard   2.05      4  0.0739
16        5        3 rsq     standard   0.427     4  0.0164
17        5        5 rmse    standard   2.02      4  0.0717
18        5        5 rsq     standard   0.442     4  0.0153
# A tibble: 1 x 7
  `lon df` `lat df` .metric .estimator  mean     n std_err
     <dbl>    <dbl> <chr>   <chr>      <dbl> <int>   <dbl>
1        5        5 rmse    standard    2.02     4  0.0717

Linear Regression Model with PCA

We can create another workflow to see how model pefromance compares using a different model. In this case we are going to perform something called Principal Component Analysis or PCA.

So what is PCA?

PCA is a widely used dimensionality reduction method (a form of unspervised machine learning). It creates new variables that capture the most variation within the data, yet reduce the data down to just a number of principal components. It does so by transforiming the data using orthogonal%20%3D%200.){target="_blank"} linear transformation. In otherwords, it creates new variables that are linear combinations of the variables within the data. Importantly these new variables are orthogonal, meaning that the new variables have zero covariance. In simpler terms, we are expessing unique types of variation within the data as new variables.

Check out this video for more information.

Let’s take a look to see what the step_pca function does to our predictors. To do so recall that we need to use the prep and juice functions of the recipes package on our recipe.

oper 1 step dummy [training] 
oper 2 step pca [training] 
The retained training set is ~ 0.13 Mb  in memory.
Rows: 584
Columns: 8
$ id    <fct> 1003.001, 1027.0001, 1033.1002, 1055.001, 1069.0003, 1073.0023,…
$ value <dbl> 9.597647, 10.800000, 11.212174, 12.375394, 10.508850, 15.591017…
$ fips  <fct> 1003, 1027, 1033, 1055, 1069, 1073, 1073, 1073, 1073, 1073, 107…
$ PC1   <dbl> -4120232815, -1570558844, -1534934869, -1388102571, -1504354268…
$ PC2   <dbl> -118909004, -346706197, 10140574, -129802253, -136385028, 23433…
$ PC3   <dbl> -72827.994, 7132.812, -56957.316, 45519.861, 36487.258, 448189.…
$ PC4   <dbl> -20385.63273, 1615.38773, -7187.37037, -15876.58640, -25852.952…
$ PC5   <dbl> -846.027447, 90.287809, 171.426032, -576.131718, -926.823714, -…

We still want to use the lm package for our regression so we can use the same model object as before:

Linear Regression Model Specification (regression)

Computational engine: lm 
══ Workflow ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()

── Preprocessor ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2 Recipe Steps

● step_dummy()
● step_pca()

── Model ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Linear Regression Model Specification (regression)

Computational engine: lm 

Remember that using workflows we dont actually need to prep our recipe, we can just fit our model directly.

Fit the cross validation samples:

Look at the performance:

# A tibble: 2 x 5
  .metric .estimator   mean     n std_err
  <chr>   <chr>       <dbl> <int>   <dbl>
1 rmse    standard   2.76       4  0.271 
2 rsq     standard   0.0527     4  0.0184

And we can compare this with our previous performance:

# A tibble: 2 x 5
  .metric .estimator  mean     n std_err
  <chr>   <chr>      <dbl> <int>   <dbl>
1 rmse    standard   2.18      4  0.0763
2 rsq     standard   0.361     4  0.0486

So we can see that our performance isn’t quite as good - especially if we look at the rmse value.

Random Forest

Now for one last recipe, we are going to predict using a decision tree method called random forests.

A decision tree is a tool to partition data based on a series of sequential binary decisions.

[source]

In our case, the random forest algorithm that we are working with does not work well when there are categorical variabels with more than 53 levels, so we will need to remove the zcta variable.

Random Forest Model Specification (regression)

Main Arguments:
  mtry = 10
  min_n = 3
Random Forest Model Specification (regression)

Main Arguments:
  mtry = 10
  min_n = 3

Computational engine: randomForest 
══ Workflow ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()

── Preprocessor ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
7 Recipe Steps

● step_string2factor()
● step_rm()
● step_rm()
● step_rm()
● step_rm()
● step_corr()
● step_nzv()

── Model ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Random Forest Model Specification (regression)

Main Arguments:
  mtry = 10
  min_n = 3

Computational engine: randomForest 

Fitting the data with just parsnip and with the workflow:

══ Workflow [trained] ═════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: rand_forest()

── Preprocessor ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
7 Recipe Steps

● step_string2factor()
● step_rm()
● step_rm()
● step_rm()
● step_rm()
● step_corr()
● step_nzv()

── Model ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Call:
 randomForest(x = as.data.frame(x), y = y, mtry = ~10, nodesize = ~3) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 10

          Mean of squared residuals: 3.016711
                    % Var explained: 58.14

Let’s take a look at the top 10 contributing variables:

Interesting, in the previous model the CMAQ values were also important, however the variable about if the monitor was located in California or not was also very predictive.

Now let’s take a look at model performance by fitting the data using cross validation:

# A tibble: 2 x 5
  .metric .estimator  mean     n std_err
  <chr>   <chr>      <dbl> <int>   <dbl>
1 rmse    standard   1.79      4  0.105 
2 rsq     standard   0.580     4  0.0377

Now let’s compare the performance of this model with the others:

# A tibble: 2 x 5
  .metric .estimator  mean     n std_err
  <chr>   <chr>      <dbl> <int>   <dbl>
1 rmse    standard   2.18      4  0.0763
2 rsq     standard   0.361     4  0.0486
# A tibble: 1 x 7
  `lon df` `lat df` .metric .estimator  mean     n std_err
     <dbl>    <dbl> <chr>   <chr>      <dbl> <int>   <dbl>
1        5        5 rmse    standard    2.02     4  0.0717
# A tibble: 2 x 5
  .metric .estimator   mean     n std_err
  <chr>   <chr>       <dbl> <int>   <dbl>
1 rmse    standard   2.76       4  0.271 
2 rsq     standard   0.0527     4  0.0184

Ok, so our first model had a mean rmse value of 2.18. The model with the lat/long degrees of freedom tuning had a mean rmse value of 2.02, thus showing some improvement. The PCA model had a mean rmse value of 2.76.

It looks like the random forest model had the lowest rmse value of 1.79.

If we tuned our random forest model based on the number of trees or the value for mtry (which is “The number of predictors that will be randomly sampled at each split when creating the tree models”), we might get a model with even better performance.

However, our cross valided mean rmse value of 1.79 is quite good because our range of true outcome values is much larger:3.4963303, 22.259123.

Final Model Performance Evaluation

Now that we have decided that we have resonable performance with the training data, we could stop here and use the yardstick package (and tune if using workflows to fit our model) to evaluate performance with our testing data.

So now we will use our random forests model to predict values for the monitors in the testing data.

Using parsnip we would need to use the baked data testing data. With the workflows package, we could use the raw testing data.

Importantly, ID variables are not delt with as nicely as with the workflows package so we would need to remove them. We did this above when created the processed training data for this model, the juiced_train data as well.

# A tibble: 292 x 1
   .pred
   <dbl>
 1 11.1 
 2 11.8 
 3 12.0 
 4 11.3 
 5 11.8 
 6 11.9 
 7 10.6 
 8 10.6 
 9  8.65
10  7.60
# … with 282 more rows
# A tibble: 292 x 5
   .pred value fips  county     id       
   <dbl> <dbl> <fct> <chr>      <fct>    
 1 11.1  11.7  1049  DeKalb     1049.1003
 2 12.0  13.1  1073  Jefferson  1073.101 
 3 11.6  12.2  1073  Jefferson  1073.2006
 4 11.3  12.2  1089  Madison    1089.0014
 5 11.5  11.4  1103  Morgan     1103.0011
 6 12.0  12.2  1121  Talladega  1121.0002
 7 10.8  10.9  4013  Maricopa   4013.4003
 8 10.7  10.6  4021  Pinal      4021.0001
 9  8.60 14.1  4023  Santa Cruz 4023.0004
10  7.59  5.83 4025  Yavapai    4025.2002
# … with 282 more rows
# A tibble: 3 x 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 rmse    standard       1.49 
2 rsq     standard       0.612
3 mae     standard       1.06 

Awesome! We can see that our rmse of 1.49 is quite similar with our testing data. We achieved quite good performance, which suggests that we would could predict other locations with more sparse monitoring based on our predictors with resonable accuracy.

We could also use the last_fit() function of the tune package to look at performance if we chose to create a workflow using the workflows package.

# A tibble: 2 x 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 rmse    standard       1.49 
2 rsq     standard       0.607

We could check out test performance using the collect_metrics() function of the tune package.

# A tibble: 2 x 3
  .metric .estimator .estimate
  <chr>   <chr>          <dbl>
1 rmse    standard       1.49 
2 rsq     standard       0.607

Here you can see the predictions for the test set (the 292 rows with predicitions out of the 876 original monitor values) also using the tune package with the collect_predictions() function.

# A tibble: 292 x 4
   id               .pred  .row value
   <chr>            <dbl> <int> <dbl>
 1 train/test split 11.1      4 11.7 
 2 train/test split 11.9     10 13.1 
 3 train/test split 11.8     12 12.2 
 4 train/test split 11.4     15 12.2 
 5 train/test split 11.5     19 11.4 
 6 train/test split 12.1     22 12.2 
 7 train/test split 10.6     30 10.9 
 8 train/test split 10.7     36 10.6 
 9 train/test split  8.54    39 14.1 
10 train/test split  7.46    40  5.83
# … with 282 more rows

Data Visualization

Our main question for this case study was:

  1. Can we predict annual average air pollution concentrations at the granularity of zip code regional levels using predictors such as data about population density, urbanization, road density, as well as, satellite pollution data and chemical modeling data?

We have indeed created a model that can predict fine particulate matter air pollution levels based on our predictor variables.

Now let’s make a plot of our predicted values and the true values.

First, let’s start by making a plot of our monitors:

We will use the following packages to create a map of the US: 1)sf 2)rnaturalearth 3)rnaturalearthdata 4)rgeos

According to this link on wikipedia, these are the latitude and longitude bounds of the contintental US.

top = 49.3457868 # north lat left = -124.7844079 # west long right = -66.9513812 # east long bottom = 24.7433195 # south lat

We will start with getting an outline of the US with the ne_countries() function of the rnaturalearth package.

[1] "sf"         "data.frame"

Now let’s add county lines.

County data is available from the maps package.

Now let’s add a fill at the county level for the true monitor values of air pollution:

Simple feature collection with 6 features and 1 field
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -88.01778 ymin: 30.24071 xmax: -85.06131 ymax: 34.2686
CRS:            EPSG:4326
               ID                           geom
1 alabama,autauga MULTIPOLYGON (((-86.50517 3...
2 alabama,baldwin MULTIPOLYGON (((-87.93757 3...
3 alabama,barbour MULTIPOLYGON (((-85.42801 3...
4    alabama,bibb MULTIPOLYGON (((-87.02083 3...
5  alabama,blount MULTIPOLYGON (((-86.9578 33...
6 alabama,bullock MULTIPOLYGON (((-85.66866 3...

Now let’s do the same with our predicted values.

Let’s grab both the testing and training fitted values so that we have as much data as possible. In this case, the output structure for the training data fit is slightly different using randomForest. The fitted values are called predicted and the broom functions like tidy() and augment() will not work. So we will manually grab the fitted training data values.

# A tibble: 292 x 5
   .pred value fips  county     id       
   <dbl> <dbl> <fct> <chr>      <fct>    
 1 11.1  11.7  1049  DeKalb     1049.1003
 2 12.0  13.1  1073  Jefferson  1073.101 
 3 11.6  12.2  1073  Jefferson  1073.2006
 4 11.3  12.2  1089  Madison    1089.0014
 5 11.5  11.4  1103  Morgan     1103.0011
 6 12.0  12.2  1121  Talladega  1121.0002
 7 10.8  10.9  4013  Maricopa   4013.4003
 8 10.7  10.6  4021  Pinal      4021.0001
 9  8.60 14.1  4023  Santa Cruz 4023.0004
10  7.59  5.83 4025  Yavapai    4025.2002
# … with 282 more rows

Summary

Let’s review everything:

We have explored gravametric fine particulate matter air pollution monitoring data. We have utilized the tidymodels ecosystem of packages to predict monitor values using a variety of predictors, also known as explanatory varaibles, including sattelite data, road density data, and population density, among others. Our model could be extended to be used to predict pollution levels in areas without monitoring, to help identify regions where populations maybe especially at risk for the health effects of air pollution.

We learned that there are two major types of what is called supervised machine learning: prediction and classification. We learned that prediction is used when the outcome variable is numeric and classifaction is performed when the outcome variable is categorical.

We performed the major steps of machine learning that we introduced in the beginning of the data analysis:

  1. Data exploration

We used a packages like skimr, summarytools, corrplot, ggcorrplot, and GGally to better understand our data. These packages gave can tell us how many missing values each variable has (if any), the class of each variable, the distribution of values for each variable, the sparsity of each variable, and the level of correlation between variables.

  1. Data splitting

We used the rsample package to first perform an initial split of our data into two peices: a training set and a testing set. The training set was used to optimize the model, while the testing set was used only to evaluate the performance of our final model. We also used the rsample package to create cross validation subsets of our training data. This allowed us to better assess the performance of our tested models using our training data.

  1. Variable assignment and pre-processing

We used the recipes package to assign variable roles (such as outcome, predictor, and id variable). We also used this package to create a recipe for pre-processing our training and testing data. This involved steps such as: step_dummy to create dummy numeric encodings of our categorical variables, step_corr to remove highly correlated variables, step_nzv to remove near zero variance variabels that would contibute little to our model and potentially add noise. We learned that once our recipe was created and prepped using prep()we could extract the pre-processed training data using juice() or our pre-processed testing data using bake(). We also learned that if we used the newer workflows package that we did not need to the prep(), juice(), or bake() functions, but that it is still useful to know how to do so if we want to look at our data and how the recipe is influencing it more deeply.

  1. Model specification, fitting, tuning and performance evaluation using the training data

We learned that the model needs to first be fit to the training data. We learned that in both classification and prediction, the model is fit to the traing data and the explanatory variables are used to estimate numeric values (in the case of prediction) or categorical values (in the case of classification) of the outcome variable of interest. We learned that we specify the model and its specifications using the parnsip package and that we also use this packae to fit the model using the fit() function. We learned that we if just use parsnip to fit the model, then we need to use the pre-processed training data (output from juice()). We learned that we can use the raw training data if we use the workflows package to create a workflow that pre-processes our data for us.

We learned that if the model fits well than the estimated values will be very similar to the true outcome variable values in our training data. We learned that we can assess model peformance using the yardstick package and the metrics() function. We also learned that we can use subsets of our training data (which we created with the rsample package) to perform cross validation to get a better estimate about the performance of our model using our training data, as we want our results to be generalizable and to perform well with other data, not just our training data. We used the fit_resamples() function of the tune package to fit our model on our different training data subsets and the collect_metrics() function (also of the tune package) to evaluate model performance using these subsets. We also learned that we can potentially improve model performance by tuning aspects about the model called hyperparameters to determine the best option for model performance. We learned that we can do this using the tune and dials packages and evaluating the performance of our model with the different hyperparameter options and our training data subsets that we used for cross validation. After we tested several different methods to model our data, we compared them to choose the best performing model as our final model.

  1. Overall model performance evaluation

Once we chose our final model, we evaluated the final model performance using the testing data. This gives us a better estimate about how well the model will predict or classify the outcome variable of interest with new independent data. Ideally one would also perform an evaluation with independent data to provide a sense of how generalizable the model is to other data sources.

We first fit our model to our testing data using either just parsnip and the pre-processed testing data (using the bake() recipes function), or our raw testing data if we used a workflow. We used the same performance evaluation functions (yardstick::metrics() and tune::collect_metrics()(when using cross validion)). We also learned how we can use the last_fit() function of the tune package if we used a workflow to get the test data performance using the initial data and the testing/training split information.

Analyses like the one in our case study are important for defining which groups could benefit the most from interventions, education, and policy changes when attempting to mitigate public health challenges. You can see in this article that many additional considerations would be involved to adequately understand the data enough to recommend policy changes.

Suggested Homework

Students can predict air pollution monitor values using a different algorithm and provide an explanation for how that algorithm works and why it may be a good choice for modeling this data.

LS0tCnRpdGxlOiAiT3BlbiBDYXNlIFN0dWRpZXMgOiBQcmVkaWN0aW5nIEFubnVhbCBBaXIgUG9sbHV0aW9uICIKY3NzOiBzdHlsZS5jc3MKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBzZWxmX2NvbnRhaW5lZDogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICB3b3JkX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKCi0tLQo8c3R5bGU+CiNUT0MgewogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL2ltZy9sb2dvLmpwZyIpOwogIGJhY2tncm91bmQtc2l6ZTogY29udGFpbjsKICBwYWRkaW5nLXRvcDogMjQwcHggIWltcG9ydGFudDsKICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Owp9Cjwvc3R5bGU+CgoKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBUUlVFLCBjb21tZW50ID0gTkEsIGVjaG8gPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGNhY2hlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgb3V0LndpZHRoID0gJzkwJScpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShrbml0cikKYGBgCgoKIyMjIyB7Lm91dGxpbmUgfQpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiODAwIHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIm1haW5wbG90LnBuZyIpKQpgYGAKCiMjIyMKCiMjIHsuZGlzY2xhaW1lcl9ibG9ja30KCioqRGlzY2xhaW1lcioqOiBUaGUgcHVycG9zZSBvZiB0aGUgW09wZW4gQ2FzZSBTdHVkaWVzXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8pe3RhcmdldD0iX2JsYW5rIn0gcHJvamVjdCBpcyAqKnRvIGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgdmFyaW91cyBkYXRhIHNjaWVuY2UgbWV0aG9kcywgdG9vbHMsIGFuZCBzb2Z0d2FyZSBpbiB0aGUgY29udGV4dCBvZiBtZXNzeSwgcmVhbC13b3JsZCBkYXRhKiouIEEgZ2l2ZW4gY2FzZSBzdHVkeSBkb2VzIG5vdCBjb3ZlciBhbGwgYXNwZWN0cyBvZiB0aGUgcmVzZWFyY2ggcHJvY2VzcywgaXMgbm90IGNsYWltaW5nIHRvIGJlIHRoZSBtb3N0IGFwcHJvcHJpYXRlIHdheSB0byBhbmFseXplIGEgZ2l2ZW4gZGF0YSBzZXQsIGFuZCBzaG91bGQgbm90IGJlIHVzZWQgaW4gdGhlIGNvbnRleHQgb2YgbWFraW5nIHBvbGljeSBkZWNpc2lvbnMgd2l0aG91dCBleHRlcm5hbCBjb25zdWx0YXRpb24gZnJvbSBzY2llbnRpZmljIGV4cGVydHMuIAoKIyMgTW90aXZhdGlvbgpBIHZhcmlldHkgb2YgZGlmZmVyZW50IHNvdXJjZXMgY29udHJpYnV0ZSBkaWZmZXJlbnQgdHlwZXMgb2YgcG9sbHV0YW50cyB0byB3aGF0IHdlIGNhbGwgYWlyIHBvbGx1dGlvbi4gClNvbWUgc291cmNlcyBhcmUgbmF0dXJhbCB3aGlsZSBvdGhlcnMgYXJlIGFudGhyb3BvZ2VuaWMgKGh1bWFuIGRlcml2ZWQpOgoKPHAgYWxpZ249ImNlbnRlciI+CiAgPGltZyB3aWR0aD0iNjAwIiBzcmM9Imh0dHBzOi8vd3d3Lm5wcy5nb3Yvc3ViamVjdHMvYWlyL2ltYWdlcy9Tb3VyY2VzX0dyYXBoaWNfSHVnZS5qcGc/bWF4d2lkdGg9MTIwMCZtYXhoZWlnaHQ9MTIwMCZhdXRvcm90YXRlPWZhbHNlIj4KPC9wPgoKIyMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vdXJsP3NhPWkmdXJsPWh0dHBzJTNBJTJGJTJGd3d3Lm5wcy5nb3YlMkZzdWJqZWN0cyUyRmFpciUyRnNvdXJjZXMuaHRtJnBzaWc9QU92VmF3MnY3QVZ4U0Y4WlNBUEVoTnVkVnRiTiZ1c3Q9MTU4NTc3MDk2NjIxNzAwMCZzb3VyY2U9aW1hZ2VzJmNkPXZmZSZ2ZWQ9MENBSVFqUnhxRndvVENQRE42NnFfeGVnQ0ZRQUFBQUFkQUFBQUFCQUQpXQoKIyMjIyBNYWpvciB0eXBlcyBvZiBhaXIgcG9sbHV0YW50cwoKMSkgKipHYXNlb3VzKiogLSBDYXJib24gTW9ub3hpZGUgKENPKSwgT3pvbmUgKE9+M34pLCBOaXRyb2dlbiBPeGlkZXMoTk8sIE5PfjJ+KSwgU3VscGhlciBEaW94aWRlIChTT34yfikKMikgKipQYXJ0aWN1bGF0ZSoqIC0gc21hbGwgbGlxdWlkcyBhbmQgc29saWRzIHN1c3BlbmRlZCBpbiB0aGUgYWlyIChpbmNsdWRlcyBsZWFkLSBjYW4gaW5jbHVkZSBjZXJ0YWluIHR5cGVzIG9mIGR1c3QpCjMpICoqRHVzdCoqIC0gc21hbGwgc29saWRzIChsYXJnZXIgdGhhbiBwYXJ0aWN1bGF0ZXMpIHRoYXQgY2FuIGJlIHN1c3BlbmRlZCBpbiB0aGUgYWlyIGZvciBzb21lIHRpbWUgYnV0IGV2ZW50dWFsbHkgc2V0dGxlCjQpICoqQmlvbG9naWNhbCoqIC0gcG9sbGVuLCBiYWN0ZXJpYSwgdmlydXNlcywgbW9sZCBzcG9yZXMKClNlZSBbaGVyZV0paHR0cDovL3d3dy5yZWRsb2dlbnYuY29tL3dvcmtlci1zYWZldHkvcGFydC0xLWR1c3QtYW5kLXBhcnRpY3VsYXRlLW1hdHRlcikgZm9yIG1vcmUgZGV0YWlsIG9uIHRoZSB0eXBlcyBvZiBwb2xsdXRhbnRzIGluIHRoZSBhaXIuCgoKIyMjIyBQYXJ0aWN1bGF0ZSBwb2xsdXRpb24gCgpBaXIgcG9sbHV0aW9uIHBhcnRpY3VsYXRlcyBhcmUgZ2VuZXJhbGx5IGRlc2NyaWJlZCBieSB0aGVpciAqKnNpemUqKi4KClRoZXJlIGFyZSAzIG1ham9yIGNhdGVnb3JpZXM6CgoxKSAqKkxhcmdlIENvYXJzZSoqIFBhcnRpY3VsYXRlIE1hdGVyIC0gaGFzIGRpYW1ldGVyIG9mID4xMCBtaWNyb21ldGVycyAoMTAgwrVtKSAKCjIpICoqQ29hcnNlKiogUGFydGljdWxhdGUgTWF0ZXIgKGNhbGxlZCAqKlBNfjEwLTIuNX4qKikgLSBoYXMgZGlhbWV0ZXIgb2YgYmV0d2VlbiAyLjUgwrVtIGFuZCAxMCDCtW0KCjMpICoqRmluZSoqIFBhcnRpY3VsYXRlIE1hdGVyIChjYWxsZWQgKipQTX4yLjV+KiopIC0gaGFzIGRpYW1ldGVyIG9mIDwgMi41IMK1bSAKCioqUE1+MTB+KiogaW5jbHVkZXMgYW55IHBhcnRpY3VsYXRlIG1hdGVyIDwxMCDCtW0gKGJvdGggY29hcnNlIGFuZCBmaW5lIHBhcnRpY3VsYXRlIG1hdGVyKQoKSGVyZSB5b3UgY2FuIHNlZSBob3cgdGhlc2Ugc2l6ZXMgY29tcGFyZSB3aXRoIGEgaHVtYW4gaGFpcjoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0gIjYwMCBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwbTIuNV9zY2FsZV9ncmFwaGljLWNvbG9yXzIuanBnIikpCmBgYAoKIyMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3LmVwYS5nb3YvcG0tcG9sbHV0aW9uL3BhcnRpY3VsYXRlLW1hdHRlci1wbS1iYXNpY3MpXQoKPCEtLSA8cCBhbGlnbj0iY2VudGVyIj4gLS0+CjwhLS0gICA8aW1nIHdpZHRoPSI1MDAiIHNyYz0iaHR0cHM6Ly93d3cuc2Vuc2lyaW9uLmNvbS9pbWFnZXMvc2Vuc2lyaW9uLXNwZWNpYWxpc3QtYXJ0aWNsZS1maWd1cmUtMS1jZGQ3MC5qcGciPiAtLT4KPCEtLSA8L3A+IC0tPgoKCjx1PlRoZSBmb2xsb3dpbmcgcGxvdCBhbmQgdGFibGUgc2hvdyB0aGUgcmVsYXRpdmUgc2l6ZXMgb2YgdGhlc2UgZGlmZmVyZW50IHBvbGx1dGFudHMgaW4gbWljcm9tZXRlcnMowrVtKTo8L3U+Cgo8cCBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI2MDAiIHNyYz0iaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy90aHVtYi9kL2RmL0FpcmJvcm5lLXBhcnRpY3VsYXRlLXNpemUtY2hhcnQuc3ZnLzgwMHB4LUFpcmJvcm5lLXBhcnRpY3VsYXRlLXNpemUtY2hhcnQuc3ZnLnBuZyI+CjwvcD4KCiMjIyMjIFtbc291cmNlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QYXJ0aWN1bGF0ZXMpXQoKCjxwIGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjUwMCIgc3JjPSJodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvZmlsZXMvQXJ0aWNsZXMvNTA1NTcwL2ZwdWJoLTA4LTAwMDE0LUhUTUwvaW1hZ2VfbS9mcHViaC0wOC0wMDAxNC10MDAyLmpwZyI+CjwvcD4KCiMjIyMjIFtbc291cmNlXShodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvYXJ0aWNsZXMvMTAuMzM4OS9mcHViaC4yMDIwLjAwMDE0L2Z1bGwpXQoKCjx1PlRoaXMgdGFibGUgc2hvd3MgaG93IGRlZXBseSBzb21lIG9mIHRoZSBzbWFsbGVyIGZpbmUgcGFydGljbGVzIGNhbiBwZW5ldHJhdGUgd2l0aGluIHRoZSBodW1hbiBib2R5OjwvdT4KCjxwIGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjUwMCIgc3JjPSJodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvZmlsZXMvQXJ0aWNsZXMvNTA1NTcwL2ZwdWJoLTA4LTAwMDE0LUhUTUwvaW1hZ2VfbS9mcHViaC0wOC0wMDAxNC10MDAxLmpwZyI+CjwvcD4KCiMjIyMjIFtbc291cmNlXShodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvYXJ0aWNsZXMvMTAuMzM4OS9mcHViaC4yMDIwLjAwMDE0L2Z1bGwpXQoKCiMjIyMgTmVnYXRpdmUgSW1wYWN0IG9mIFBhcnRpY3VsYXRlIEV4cG9zdXJlIG9uIEhlYWx0aCAKCkV4cG9zdXJlIHRvIGFpciBwb2xsdXRpb24gaXMgYXNzb2NpYXRlZCB3aXRoIGhpZ2hlciByYXRlcyBvZiBbbW9ydGFsaXR5XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1NzgzMTg2Lyl7dGFyZ2V0PSJfYmxhbmsifSBpbiBvbGRlciBhZHVsdHMgYW5kIGlzIGtub3duIHRvIGJlIGEgcmlzayBmYWN0b3IgZm9yIG1hbnkgZGlzZWFzZXMgYW5kIGNvbmRpdGlvbnMgaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZCB0bzoKCjEpIFtBc3RobWFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcHVibWVkLzI5MjQzOTM3KXt0YXJnZXQ9Il9ibGFuayJ9IC0gZmluZSBwYXJ0aWNsZSBleHBvc3VyZSAoKipQTX4yLjV+KiopIHdhcyBmb3VuZCB0byBiZSBhc3NvY2lhdGVkIHdpdGggaGlnaGVyIHJhdGVzIG9mIGFzdGhtYSBpbiBjaGlsZHJlbgoyKSBbSW5mbGFtbWF0aW9uIGluIHR5cGUgMSBkaWFiZXRlc10oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMzE0MTk3NjUpe3RhcmdldD0iX2JsYW5rIn0gLSBmaW5lIHBhcnRpY2xlIGV4cG9zdXJlICgqKlBNfjIuNX4qKikgZnJvbSB0cmFmZmljLXJlbGF0ZWQgYWlyIHBvbGx1dGlvbiB3YXMgYXNzb2NpYXRlZCB3aXRoIGluY3JlYXNlZCBtZWFzdXJlcyBvZiBpbmZsYW1tYXRvcnkgbWFya2VycyBpbiB5b3V0aHMgd2l0aCB0eXBlIDEgZGlhYmV0ZXMKMykgW0x1bmcgZnVuY3Rpb24gYW5kIGVtcGh5c2VtYV0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMzE0MDgxMzUpe3RhcmdldD0iX2JsYW5rIn0gLSBoaWdoZXIgY29uY2VudHJhdGlvbnMgb2Ygb3pvbmUgKE9+M34pLCBuaXRyb2dlbiBveGlkZXMgKE5Pfnh+KSwgYmxhY2sgY2FyYm9uLCBhbmQgZmluZSBwYXJ0aWNsZSBleHBvc3VyZSAqKlBNfjIuNX4qKiAsIGF0IHN0dWR5IGJhc2VsaW5lIHdlcmUgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggZ3JlYXRlciBpbmNyZWFzZXMgaW4gcGVyY2VudCBlbXBoeXNlbWEgcGVyIDEwIHllYXJzIAo0KSBbTG93IGJpcnRod2VpZ2h0XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8zMTM4NjY0Myl7dGFyZ2V0PSJfYmxhbmsifSAtIGZpbmUgcGFydGljbGUgZXhwb3N1cmUoKipQTX4yLjV+KiopIHdhcyBhc3NvY2lhdGVkIHdpdGggbG93ZXIgYmlydGggd2VpZ2h0IGluIGZ1bGwtdGVybSBsaXZlIGJpcnRocwo1KSBbVmlyYWwgSW5mZWN0aW9uXShodHRwczovL3d3dy50YW5kZm9ubGluZS5jb20vZG9pL2Z1bGwvMTAuMTA4MC8wODk1ODM3MDcwMTY2NTQzNCl7dGFyZ2V0PSJfYmxhbmsifSAtIGhpZ2hlciByYXRlcyBvZiBpbmZlY3Rpb24gYW5kIGluY3JlYXNlZCBzZXZlcml0eSBvZiBpbmZlY3Rpb24gYXJlIGFzc29jaWF0ZWQgd2l0aCBoaWdoZXIgZXhwb3N1cmVzIHRvIHBvbGx1dGlvbiBsZXZlbHMgaW5jbHVkaW5nIGZpbmUgcGFydGljbGUgZXhwb3N1cmUgKCoqUE1+Mi41fioqKQoKU2VlIHRoaXMgW3JldmlldyBhcnRpY2xlXShodHRwczovL3d3dy5mcm9udGllcnNpbi5vcmcvYXJ0aWNsZXMvMTAuMzM4OS9mcHViaC4yMDIwLjAwMDE0L2Z1bGwpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgc291cmNlcyBvZiBhaXIgcG9sbHV0aW9uIGFuZCB0aGUgaW5mbHVlbmNlIG9mIGFpciBwb2xsdXRpb24gb24gaGVhbHRoLgoKIyMjIyBTcGFyc2UgTW9uaXRvcmluZyBpcyBQcm9ibGVtYXRpYyBmb3IgUHVibGljIEhlYWx0aAoKSGlzdG9yaWNhbGx5IGVwaWRlbWlvbG9naWNhbCBzdHVkaWVzIHdvdWxkIGFzc2VzcyB0aGUgaW5mbHVlbmNlIG9mIGFpciBwb2xsdXRpb24gb24gaGVhbHRoIG91dGNvbWVzIGJ5IHJlbHlpbmcgb24gYSBudW1iZXIgb2YgbW9uaXRvcnMgbG9jYXRlZCBhcm91bmQgdGhlIGNvdW50cnkuIEhvd2V2ZXIgYXMgY2FuIGJlIHNlZW4gaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUsIHRoZXNlIG1vbml0b3JzIHJlbWFpbiB0byBiZSByZWxhdGl2ZWx5IHNwYXJzZSBpbiBjZXJ0YWluIHJlZ2lvbnMgb2YgdGhlIGNvdW50cnkuIEZ1cnRoZXJtb3JlLCBkcmFtYXRpYyBkaWZmZXJlbmNlcyBpbiBwb2xsdXRpb24gcmF0ZXMgY2FuIGJlIHNlZW4gZXZlbiB3aXRoaW4gdGhlIHNhbWUgY2l0eS4KCjxwIGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjQwMCIgc3JjPSJodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM0MTM3MjcyL2Jpbi8xNDc2LTA2OVgtMTMtNjMtMS5qcGciPgo8L3A+CgojIyMjIyBbW3NvdXJjZV0oaHR0cHM6Ly9laGpvdXJuYWwuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni8xNDc2LTA2OVgtMTMtNjMpXQoKVGhpcyBsYWNrIG9mIGdyYW51bGFyaXR5IGluIGFpciBwb2xsdXRpb24gbW9uaXRvcmluZyBoYXMgaGluZGVyZWQgb3VyIGFiaWxpdHkgdG8gZGlzY2VybiB0aGUgZnVsbCBpbXBhY3Qgb2YgYWlyIHBvbGx1dGlvbiBvbiBoZWFsdGggYW5kIHRvIGlkZW50aWZ5IGF0LXJpc2sgbG9jYXRpb25zLiAKCgojIyMjIE1hY2hpbmUgTGVhcm5pbmcgT2ZmZXJzIGEgU29sdXRpb24KCkFuIFthcnRpY2xlXShodHRwczovL2Voam91cm5hbC5iaW9tZWRjZW50cmFsLmNvbS9hcnRpY2xlcy8xMC4xMTg2LzE0NzYtMDY5WC0xMy02Myl7dGFyZ2V0PSJfYmxhbmsifSBwdWJsaXNoZWQgaW4gdGhlICpFbnZpcm9ubWVudGFsIEhlYWx0aCogam91cm5hbCBkZWFsdCB3aXRoIHRoaXMgaXNzdWUgYnkgdXNpbmcgZGF0YSBhYm91dCBwb3B1bGF0aW9uIGRlbnNpdHksIHJvYWQgZGVuc2l0eSwgYW1vbmcgb3RoZXIgZmVhdHVyZXMgdG8gbW9kZWwgb3IgcHJlZGljdCBhaXIgcG9sbHV0aW9uIGxldmVscyBhdCBhIG1vcmUgbG9jYWxpemVkIHNjYWxlIHVzaW5nIG1hY2hpbmUgbGVhcm5pbmcgbWV0aG9kcy4gCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9ICI4MDAgcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAidGhlcGFwZXIucG5nIikpCmBgYAoKIyMjIyB7LnJlZmVyZW5jZV9ibG9ja30KWWFub3NreSwgSi4gRC4gZXQgYWwuIFNwYXRpby10ZW1wb3JhbCBtb2RlbGluZyBvZiBwYXJ0aWN1bGF0ZSBhaXIgcG9sbHV0aW9uIGluIHRoZSBjb250ZXJtaW5vdXMgVW5pdGVkIFN0YXRlcyB1c2luZyBnZW9ncmFwaGljIGFuZCBtZXRlb3JvbG9naWNhbCBwcmVkaWN0b3JzLiAqRW52aXJvbiBIZWFsdGgqIDEzLCA2MyAoMjAxNCkuCgojIyMjCgpUaGUgYXV0aG9ycyBvZiB0aGlzIGFydGljbGUgc3RhdGUgdGhhdDoKCj4gIkV4cG9zdXJlIHRvIGF0bW9zcGhlcmljIHBhcnRpY3VsYXRlIG1hdHRlciAoUE0pIHJlbWFpbnMgYW4gaW1wb3J0YW50IHB1YmxpYyBoZWFsdGggY29uY2VybiwKYWx0aG91Z2ggaXQgcmVtYWlucyBkaWZmaWN1bHQgdG8gcXVhbnRpZnkgYWNjdXJhdGVseSBhY3Jvc3MgbGFyZ2UgZ2VvZ3JhcGhpYyBhcmVhcyB3aXRoIHN1ZmZpY2llbnRseSBoaWdoIHNwYXRpYWwKcmVzb2x1dGlvbi4gUmVjZW50IGVwaWRlbWlvbG9naWMgYW5hbHlzZXMgaGF2ZSBkZW1vbnN0cmF0ZWQgdGhlIGltcG9ydGFuY2Ugb2Ygc3BhdGlhbGx5LSBhbmQgdGVtcG9yYWxseS1yZXNvbHZlZApleHBvc3VyZSBlc3RpbWF0ZXMsIHdoaWNoIHNob3cgbGFyZ2VyIFBNLW1lZGlhdGVkIGhlYWx0aCBlZmZlY3RzIGFzIGNvbXBhcmVkIHRvIG5lYXJlc3QgbW9uaXRvciBvcgpjb3VudHktc3BlY2lmaWMgYW1iaWVudCBjb25jZW50cmF0aW9ucy4iIAoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0gIjcwMCBweCIsIGV2YWwgPSBGQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImRlYXRocy5wbmciKSkKYGBgCgpUaGUgYXJ0aWNsZSBhYm92ZSBleHBsYWlucyB0aGF0IG1hY2hpbmUgbGVhcm5pbmcgbWV0aG9kcyBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IGFpciBwb2xsdXRpb24gbGV2ZWxzIHdoZW4gdHJhZGl0aW9uYWwgbW9uaXRvcmluZyBzeXN0ZW1zIGFyZSBub3QgYXZhaWxhYmxlIGluIGEgcGFydGljdWxhciBhcmVhIG9yIHdoZW4gdGhlcmUgaXMgbm90IGVub3VnaCBzcGF0aWFsIGdyYW51bGFyaXR5IHdpdGggY3VycmVudCBtb25pdG9yaW5nIHN5c3RlbXMuIFdlIHdpbGwgdXNlIHNpbWlsYXIgbWV0aG9kcyB0byBwcmVkaWN0IGFubnVhbCBhaXIgcG9sbHV0aW9uIGxldmVscyBzcGF0aWFsbHkgd2l0aGluIHRoZSBVUy4KCgojIyMgTWFpbiBRdWVzdGlvbnMKCiMjIyMgey5tYWluX3F1ZXN0aW9uX2Jsb2NrfQo8Yj48dT4gT3VyIG1haW4gcXVlc3Rpb246IDwvdT48L2I+CgoxKSBDYW4gd2UgcHJlZGljdCBhbm51YWwgYXZlcmFnZSBhaXIgcG9sbHV0aW9uIGNvbmNlbnRyYXRpb25zIGF0IHRoZSBncmFudWxhcml0eSBvZiB6aXAgY29kZSByZWdpb25hbCBsZXZlbHMgdXNpbmcgcHJlZGljdG9ycyBzdWNoIGFzIGRhdGEgYWJvdXQgcG9wdWxhdGlvbiBkZW5zaXR5LCB1cmJhbml6YXRpb24sIHJvYWQgZGVuc2l0eSwgYXMgd2VsbCBhcywgc2F0ZWxsaXRlIHBvbGx1dGlvbiBkYXRhIGFuZCBjaGVtaWNhbCBtb2RlbGluZyBkYXRhPwoKIyMjIwoKIyMjIExlYXJuaW5nIE9iamVjdGl2ZXMgCgpJbiB0aGlzIGNhc2Ugc3R1ZHksIHdlIHdpbGwgd2FsayB5b3UgdGhyb3VnaCBpbXBvcnRpbmcgZGF0YSBmcm9tIENTViBmaWxlcyBhbmQgcGVyZm9ybWluZyBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZHMgdG8gcHJlZGljdCBvdXIgb3V0Y29tZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCAoaW4gdGhpcyBjYXNlIGFubnVhbCBmaW5lIHBhcnRpY2xlIGFpciBwb2xsdXRpb24gZXN0aW1hdGVzKS4gV2Ugd2lsbCBlc3BlY2lhbGx5IGZvY3VzIG9uIHVzaW5nIHBhY2thZ2VzIGFuZCBmdW5jdGlvbnMgZnJvbSB0aGUgW2BUaWR5dmVyc2VgXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSwgYW5kIG1vcmUgc3BlY2lmaWNhbGx5IHRoZSBbYHRpZHltb2RlbHNgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdGlkeW1vZGVscy90aWR5bW9kZWxzLnBkZil7dGFyZ2V0PSJfYmxhbmsifSBwYWNrYWdlL2Vjb3N5c3RlbSBwcmltYXJpbHkgZGV2ZWxvcGVkIGFuZCBtYWludGFpbmVkIGJ5IFtNYXggS3Vobl0oaHR0cHM6Ly9yZXNvdXJjZXMucnN0dWRpby5jb20vYXV0aG9ycy9tYXgta3Vobil7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW0RhdmlzIFZhdWdoYW5dKGh0dHBzOi8vcmVzb3VyY2VzLnJzdHVkaW8uY29tL2F1dGhvcnMvZGF2aXMtdmF1Z2hhbil7dGFyZ2V0PSJfYmxhbmsifS4gVGhpcyBwYWNrYWdlIGxvYWRzIG1vcmUgbW9kZWxpbmcgcmVsYXRlZCBwYWNrYWdlcyBsaWtlIGByc2FtcGxlYCwgYHJlY2lwZXNgLCBgcGFyc25pcGAsIGB5YXJkc3RpY2tgLCAgYW5kIGBkaWFsc2AuIFdlIHdpbGwgYWxzbyBicmllZmx5IGNvdmVyIHRoZSBgd29ya2Zsb3dzYCBhbmQgYHR1bmVgIHBhY2thZ2VzLiBUaGUgdGlkeXZlcnNlIGlzIGEgbGlicmFyeSBvZiBwYWNrYWdlcyBjcmVhdGVkIGJ5IFJTdHVkaW8uIFdoaWxlIHNvbWUgc3R1ZGVudHMgbWF5IGJlIGZhbWlsaWFyIHdpdGggcHJldmlvdXMgUiBwcm9ncmFtbWluZyBwYWNrYWdlcywgdGhlc2UgcGFja2FnZXMgbWFrZSBkYXRhIHNjaWVuY2UgaW4gUiBlc3BlY2lhbGx5IGVmZmljaWVudC4KCgpgYGB7ciwgb3V0LndpZHRoID0gIjIwJSIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly90aWR5dmVyc2UudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpCmBgYAoKYGBge3IsIG91dC53aWR0aCA9ICIxMDBweCIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9wYnMudHdpbWcuY29tL21lZGlhL0RrQkZwU3NXNEFJeXlJTi5wbmciKQpgYGAKCgpXZSB3aWxsIGJlZ2luIGJ5IGxvYWRpbmcgdGhlIHBhY2thZ2VzIHRoYXQgd2Ugd2lsbCBuZWVkOgoKYGBge3J9CmxpYnJhcnkoaGVyZSkKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeShza2ltcikKbGlicmFyeShzdW1tYXJ5dG9vbHMpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGdnY29ycnBsb3QpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHRpZHltb2RlbHMpIyBicm9vbSwgZGlhbHMsIGluZmVyLCBwYXJzbmlwLCBwdXJyciwgcmVjaXBlcywgcnNhbXBsZSwgdGliYmxlLCB5YXJkc3RpY2sKbGlicmFyeSh3b3JrZmxvd3MpCmxpYnJhcnkodmlwKQpsaWJyYXJ5KHR1bmUpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeSh0aWR5cikKCgoKIyBsaWJyYXJ5KHN0cmluZ3IpCiMgbGlicmFyeShwdXJycikKIyBsaWJyYXJ5KHRpYmJsZSkKIyBsaWJyYXJ5KHRpZHlyKQojIGxpYnJhcnkoZ2dwbG90MikKIyBsaWJyYXJ5KGdncHVicikKIyBsaWJyYXJ5KGZvcmNhdHMpCiMgbGlicmFyeShsbWVyVGVzdCkKIyBsaWJyYXJ5KGNhcikKIyBsaWJyYXJ5KGdnaXJhcGgpCiMgbGlicmFyeShnZ2ZvcmNlKQojIGxpYnJhcnkodmlyaWRpcykKIyBsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKCiBQYWNrYWdlICAgfCBVc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCi0tLS0tLS0tLS0gfC0tLS0tLS0tLS0tLS0KW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjL2hlcmVfaGVyZSl7dGFyZ2V0PSJfYmxhbmsifSAgICAgICB8IHRvIGVhc2lseSBsb2FkIGFuZCBzYXZlIGRhdGEKW3JlYWRyXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBpbXBvcnQgdGhlIENTViBmaWxlIGRhdGEKW2RwbHlyXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byB2aWV3L2FycmFuZ2UvZmlsdGVyL3NlbGVjdC9jb21wYXJlIHNwZWNpZmljIHN1YnNldHMgb2YgdGhlIGRhdGEgCltza2ltcl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NraW1yL2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGdldCBhbiBvdmVydmlldyBvZiBkYXRhCltzdW1tYXJ5dG9vbHNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9za2ltci9pbmRleC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBnZXQgYW4gb3ZlcnZpZXcgb2YgZGF0YSBpbiBhIGRpZmZlcmVudCBzdHlsZQpbbWFncml0dHJdKGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9tYWdyaXR0ci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byB1c2UgdGhlIGAlPD4lYCBwaXBwaW5nIG9wZXJhdG9yIApbY29ycnBsb3RdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb3JycGxvdC92aWduZXR0ZXMvY29ycnBsb3QtaW50cm8uaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIG1ha2UgbGFyZ2UgY29ycmVsYXRpb24gcGxvdHMKW2dnY29ycnBsb3RdKGh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS9nZ2NvcnJwbG90LXZpc3VhbGl6YXRpb24tb2YtYS1jb3JyZWxhdGlvbi1tYXRyaXgtdXNpbmctZ2dwbG90Mil8IGFsc28gdG8gbWFrZSBsYXJnZSBjb3JyZWxhdGlvbiBwbG90cwpbR0dhbGx5XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvR0dhbGx5L0dHYWxseS5wZGYpe3RhcmdldD0iX2JsYW5rIn0gfCB0byBtYWtlIHNtYWxsZXIgY29ycmVsYXRpb24gcGxvdHMgIApbcnNhbXBsZV0oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby9yc2FtcGxlL2FydGljbGVzL0Jhc2ljcy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byBzcGxpdCB0aGUgZGF0YSBpbnRvIHRlc3RpbmcgYW5kIHRyYWluaW5nIHNldHMgYW5kIHRvIHNwbGl0IHRoZSB0cmFpbmluZyBzZXQgZm9yIGNyb3NzLXZhbGlkYXRpb24gIApbcmVjaXBlc10oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby9yZWNpcGVzLyl7dGFyZ2V0PSJfYmxhbmsifSAgIHwgdG8gcHJlLXByb2Nlc3MgZGF0YSBmb3IgbW9kZWxpbmcgaW4gYSB0aWR5IGFuZCByZXByb2R1Y2libGUgd2F5IGFuZCB0byBleHRyYWN0IHByZS1wcm9jZXNzZWQgZGF0YSAobWFqb3IgZnVuY3Rpb25zIGFyZSBgcmVjaXBlKClgICwgYHByZXAoKWAgYW5kIHZhcmlvdXMgdHJhbnNmb3JtYXRpb24gYHN0ZXBfKigpYCBmdW5jdGlvbnMsIGFzIHdlbGwgYXMgYGp1aWNlKClgIC0gZXh0cmFjdHMgZmluYWwgcHJlcHJvY2Vzc2VkIHRyYWluaW5nIGRhdGEgYW5kIGBiYWtlKClgIC0gYXBwbGllcyByZWNpcGUgc3RlcHMgdG8gdGVzdGluZyBkYXRhKS4gU2VlIFtoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcmVjaXBlcy9yZWNpcGVzLnBkZil7dGFyZ2V0PSJfYmxhbmsifSAgZm9yIG1vcmUgaW5mby4KW3BhcnNuaXBdKGh0dHBzOi8vdGlkeW1vZGVscy5naXRodWIuaW8vcGFyc25pcC8pe3RhcmdldD0iX2JsYW5rIn0gICB8IGFuIGludGVyZmFjZSB0byBjcmVhdGUgbW9kZWxzIChtYWpvciBmdW5jdGlvbnMgYXJlICBgZml0KClgLCBgc2V0X2VuZ2luZSgpYCkKW3lhcmRzdGlja10oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby95YXJkc3RpY2svKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgbW9kZWxzCiAKCgpbc3RyaW5ncl0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvc3RyaW5nci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gbWFuaXB1bGF0ZSB0aGUgdGV4dCB3aXRoaW4gdGhlIFBERiBvZiB0aGUgZGF0YQoKW3B1cnJyXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBwZXJmb3JtIGZ1bmN0aW9ucyBvbiBhbGwgY29sdW1ucyBvZiBhIHRpYmJsZQpbdGliYmxlXShodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgfCB0byBjcmVhdGUgZGF0YSBvYmplY3RzIHRoYXQgd2UgY2FuIG1hbmlwdWxhdGUgd2l0aCBkcGx5ci9zdHJpbmdyL3RpZHlyL3B1cnJyClt0aWR5cl0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICAgIHwgdG8gc2VwYXJhdGUgZGF0YSB3aXRoaW4gYSBjb2x1bW4gaW50byBtdWx0aXBsZSBjb2x1bW5zCltnZ3Bsb3QyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgfCB0byBtYWtlIHZpc3VhbGl6YXRpb25zIHdpdGggbXVsdGlwbGUgbGF5ZXJzCltnZ3B1YnJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ3B1YnIvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIGVhc2lseSBhZGQgcmVncmVzc2lvbiBsaW5lIGVxdWF0aW9ucyB0byBwbG90cwpbZm9yY2F0c10oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gY2hhbmdlIGRldGFpbHMgYWJvdXQgZmFjdG9ycyAoY2F0ZWdvcmljYWwgdmFyaWFibGVzKQpbbG1lclRlc3RdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9sbWVyVGVzdC9sbWVyVGVzdC5wZGYpfCB0byBwZXJmb3JtIGxpbmVhciBtaXhlZCBtb2RlbCB0ZXN0aW5nCltjYXJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jYXIvY2FyLnBkZil8IHRvIHBlcmZvcm0gTGV2ZW5lJ3MgVGVzdCBvZiBIb21vZ2VuZWl0eSBvZiBWYXJpYW5jZXMKW2dnaXJhcGhdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2lyYXBoL2luZGV4Lmh0bWwpfCB0byBtYWtlIHBsb3RzIGludGVyYWN0aXZlCltnZ2ZvcmNlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2dmb3JjZS9nZ2ZvcmNlLnBkZil8IHRvIG1vZGlmeSBmYWNldHMgaW4gcGxvdHMKW3ZpcmlkaXNdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy92aXJpZGlzL3ZpZ25ldHRlcy9pbnRyby10by12aXJpZGlzLmh0bWwpfCB0byBwbG90IGluIGNvbG9yIHBhbGV0dGUgdGhhdCBpcyBlYXNpbHkgaW50ZXJwcmV0ZWQgYnkgY29sb3JibGluZCBpbmRpdmlkdWFscwpbY293cGxvdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nvd3Bsb3QvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gYWxsb3cgcGxvdHMgdG8gYmUgY29tYmluZWQKX19fCgoKClRoZSBmaXJzdCB0aW1lIHdlIHVzZSBhIGZ1bmN0aW9uLCB3ZSB3aWxsIHVzZSB0aGUgYDo6YCB0byBpbmRpY2F0ZSB3aGljaCBwYWNrYWdlIHdlIGFyZSB1c2luZy4gVW5sZXNzIHdlIGhhdmUgb3ZlcmxhcHBpbmcgZnVuY3Rpb24gbmFtZXMsIHRoaXMgaXMgbm90IG5lY2Vzc2FyeSwgYnV0IHdlIHdpbGwgaW5jbHVkZSBpdCBoZXJlIHRvIGJlIGluZm9ybWF0aXZlIGFib3V0IHdoZXJlIHRoZSBmdW5jdGlvbnMgd2Ugd2lsbCB1c2UgY29tZSBmcm9tLgoKCiMjIyBDb250ZXh0CgpUaGUgW1N0YXRlIG9mIEdsb2JhbCBBaXJdKGh0dHBzOi8vd3d3LnN0YXRlb2ZnbG9iYWxhaXIub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSBpcyBhIHJlcG9ydCByZWxlYXNlZCBldmVyeSB5ZWFyIHRvIGNvbW11bmljYXRlIHRoZSBpbXBhY3Qgb2YgYWlyIHBvbGx1dGlvbiBvbiBwdWJsaWMgaGVhbHRoLiAKClRoZSBbU3RhdGUgb2YgR2xvYmFsIEFpciAyMDE5IHJlcG9ydF0oaHR0cHM6Ly93d3cuc3RhdGVvZmdsb2JhbGFpci5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9zb2dhXzIwMTlfcmVwb3J0LnBkZil7dGFyZ2V0PSJfYmxhbmsifQp3aGljaCB1c2VzIGRhdGEgZnJvbSAyMDE3IHN0YXRlZCB0aGF0OgoKPiBBaXIgcG9sbHV0aW9uIGlzIHRoZSAqKmZpZnRoKiogbGVhZGluZyByaXNrIGZhY3RvciBmb3IgbW9ydGFsaXR5IHdvcmxkd2lkZS4gSXQgaXMgcmVzcG9uc2libGUgZm9yIG1vcmUKZGVhdGhzIHRoYW4gbWFueSBiZXR0ZXIta25vd24gcmlzayBmYWN0b3JzIHN1Y2ggYXMgbWFsbnV0cml0aW9uLCBhbGNvaG9sIHVzZSwgYW5kIHBoeXNpY2FsIGluYWN0aXZpdHkuCkVhY2ggeWVhciwgKiptb3JlKiogcGVvcGxlIGRpZSBmcm9tIGFpciBwb2xsdXRpb27igJNyZWxhdGVkIGRpc2Vhc2UgdGhhbiBmcm9tIHJvYWQgKip0cmFmZmljIGluanVyaWVzKiogb3IgKiptYWxhcmlhKiouCgo8cCBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI2MDAiIHNyYz0iaHR0cHM6Ly93d3cuaGVhbHRoZWZmZWN0cy5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9Tb0dBLUZpZ3VyZXMtMDEuanBnIj4KPC9wPgoKVGhlIHJlcG9ydCBhbHNvIHN0YXRlZCB0aGF0OgoKPkluIDIwMTcsIGFpciBwb2xsdXRpb24gaXMgZXN0aW1hdGVkIHRvIGhhdmUgY29udHJpYnV0ZWQgdG8gY2xvc2UgdG8gNSBtaWxsaW9uCmRlYXRocyBnbG9iYWxseSDigJQgbmVhcmx5ICoqMSBpbiBldmVyeSAxMCBkZWF0aHMqKi4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iODAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCIyMDE3ZGVhdGhzLnBuZyIpKQpgYGAKIyMjIyMgW1tzb3VyY2VdXShodHRwczovL3d3dy5zdGF0ZW9mZ2xvYmFsYWlyLm9yZy9zaXRlcy9kZWZhdWx0L2ZpbGVzL3NvZ2FfMjAxOV9mYWN0X3NoZWV0LnBkZikKClRoZSBbU3RhdGUgb2YgR2xvYmFsIEFpciAyMDE4IHJlcG9ydF0oaHR0cHM6Ly93d3cuc3RhdGVvZmdsb2JhbGFpci5vcmcvc2l0ZXMvZGVmYXVsdC9maWxlcy9zb2dhLTIwMTgtcmVwb3J0LnBkZil7dGFyZ2V0PSJfYmxhbmsifSAgdXNpbmcgZGF0YSBmcm9tIDIwMTYgd2hpY2ggc2VwYXJhdGVkIGRpZmZlcmVudCB0eXBlcyBvZiBhaXIgcG9sbHV0aW9uLCBmb3VuZCB0aGF0ICoqcGFydGljdWxhdGUgcG9sbHV0aW9uIHdhcyBwYXJ0aWN1bGFybHkgYXNzb2NpYXRlZCB3aXRoIG1vcnRhbGl0eSoqLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI4MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsIjIwMTdtb3J0YWxpdHkucG5nIikpCmBgYAoKVGhlIDIwMTkgcmVwb3J0IHNob3dzIHRoYXQgdGhlIGhpZ2hlc3QgbGV2ZWxzIG9mIGZpbmUgcGFydGljdWxhdGUgcG9sbHV0aW9uIG9jY3VycyBpbiBBZnJpY2EgYW5kIEFzaWEgYW5kIHRoYXQ6Cgo+IE1vcmUgdGhhbiAqKjkwJSoqIG9mIHBlb3BsZSB3b3JsZHdpZGUgbGl2ZSBpbiBhcmVhcyAqKmV4Y2VlZGluZyoqIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIChXSE8pICoqR3VpZGVsaW5lKiogZm9yIGhlYWx0aHkgYWlyLiBNb3JlIHRoYW4gaGFsZiBsaXZlIGluIGFyZWFzIHRoYXQgZG8gbm90IGV2ZW4gbWVldCBXSE/igJlzIGxlYXN0LXN0cmluZ2VudCBhaXIgcXVhbGl0eSB0YXJnZXQuCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjgwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwiUE13b3JsZC5wbmciKSkKYGBgCgpMb29raW5nIGF0IHRoZSBVUyBzcGVjaWZpY2FsbHksIGFpciBwb2xsdXRpb24gbGV2ZWxzIGFyZSBnZW5lcmFsbHkgaW1wcm92aW5nLiBUaGUgVVMgRW52aXJvbm1lbnRhbCBQcm90ZWN0aW9uIEFnZW5jeSAoRVBBKSBhbHNvIHJlbGVhc2VzIGEgcmVwb3J0IGFib3V0IGFpciBwb2xsdXRpb24gbGV2ZWxzIGNhbGxlZCBbKk91ciBOYXRpb24ncyBBaXIqXShodHRwczovL2dpc3B1Yi5lcGEuZ292L2Fpci90cmVuZHNyZXBvcnQvMjAxOS8jaG9tZSl7dGFyZ2V0PSJfYmxhbmsifS4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJVUy5wbmciKSkKYGBgCgojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vZ2lzcHViLmVwYS5nb3YvYWlyL3RyZW5kc3JlcG9ydC8yMDE5L2RvY3VtZW50YXRpb24vQWlyVHJlbmRzX0ZseWVyLnBkZikKCkhvd2V2ZXIsIGFpciBwb2xsdXRpb24gKipjb250aW51ZXMgdG8gY29udHJpYnV0ZSB0byBoZWFsdGggcmlzayBmb3IgQW1lcmljYW5zKiosIGluIHBhcnRpY3VsYXIgaW4gKipyZWdpb25zIHdpdGggaGlnaGVyIHRoYW4gbmF0aW9uYWwgYXZlcmFnZSByYXRlcyoqIG9mIHBvbGx1dGlvbiB0aGF0IGFjdHVhbGx5IGF0IHRpbWUgZXhjZWVkIHRoZSB3b3JsZCBoZWFsdGggb3JnYW5pemF0aW9uJ3MgcmVjb21tZW5kZWQgbGV2ZWwuIFRodXMgaXQgaXMgbmVjZXNzYXJ5IHRvIG9idGFpbiBoaWdoIHNwYXRpYWwgZ3JhbnVsYXJpdHkgaW4gZXN0aW1hdGVzIG9mIGFpciBwb2xsdXRpb24gaW4gb3JkZXIgdG8gaWRlbnRpZnkgbG9jYXRpb25zIHdoZXJlIHBvcHVsYXRpb25zIGFyZSBleHBlcmllbmNpbmcgaGFybWZ1bCBsZXZlbHMgb2YgZXhwb3N1cmUuCgoKWW91IGNhbiBzZWUgdGhhdCBjdXJyZW50IGFpciBxdWFsaXR5IGNvbmRpdGlvbnMgYXQgdGhpcyBbd2Vic2l0ZV0oaHR0cHM6Ly9hcWljbi5vcmcvY2l0eS91c2EvKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCB5b3Ugd2lsbCBub3RpY2UgdmFyaWF0aW9uIGFjcm9zcyBkaWZmZXJlbnQgY2l0aWVzLgoKSGVyZSB3ZXJlIHRoZSBjb25kaXRpb25zIGluIFRvcGVrYSBLYW5zYXMgd2hlbiB0aGlzIHdhcyB3cml0dGVuOgoKYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIkthbnNhcy5wbmciKSkKYGBgCgpJdCByZXBvcnRzIHBhcnRpY3VsYXRlIHZhbHVlcyB1c2luZyB3aGF0IGlzIGNhbGxlZCB0aGUgW0FpciBRdWFsaXR5IEluZGV4XShodHRwczovL3d3dy5haXJub3cuZ292L2luZGV4LmNmbT9hY3Rpb249YXFpYmFzaWNzLmFxaSl7dGFyZ2V0PSJfYmxhbmsifSBzY2FsZSAoQVFJKSwgdGhpcyBbY2FsY3VsYXRvcl0oaHR0cHM6Ly9haXJub3cuZ292L2luZGV4LmNmbT9hY3Rpb249YWlybm93LmNhbGN1bGF0b3Ipe3RhcmdldD0iX2JsYW5rIn0gaW5kaWNhdGVzIHRoYXQgMTE0IEFRSSBpcyBlcXVpdmFsZW50IHRvIDQwLjcgdWcvbV4zXiBhbmQgaXMgY29uc2lkZXJlZCB1bmhlYWx0aHkgZm9yIHNlbnNpdGl2ZSBpbmRpdmlkdWFscy4gVGh1cyBzb21lIGFyZWFzIHZlcnkgbXVjaCBleGNlZWQgdGhlIFdvcmxkIEhlYWx0aCBPcmdhbml6YXRpb24gKFdITykgIGFubnVhbCBleHBvc3VyZSBndWlkZWxpbmUgKDEwIHVnL21eM14pIGF0IGNlcnRhaW4gdGltZXMgYW5kIHRoaXMgbWF5IGFkdmVyc2VseSBhZmZlY3QgdGhlIGhlYWx0aCBvZiBwZW9wbGUgbGl2aW5nIGluIHRoZXNlIGxvY2F0aW9ucy4KCkZ1cnRoZXJtb3JlLCBhZHZlcnNlIGhlYWx0aCBlZmZlY3RzIGhhdmUgYmVlbiBhc3NvY2lhdGVkIHdpdGggcG9wdWxhdGlvbnMgZXhwZXJpZW5jaW5nIGhpZ2hlciBwb2xsdXRpb24gZXhwb3N1cmUgZGVzcGl0ZSB0aGUgbGV2ZWxzIGJlaW5nIGJlbG93IHN1Z2dlc3RlZCBndWlkZWxpbmVzLiBTZWNvbmRseSwgaXQgYXBwZWFycyB0aGF0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGUgcGFydGljdWxhdGUgbWF0ZXIgYW5kIHRoZSBpbmZsdWVuY2Ugb2Ygb3RoZXIgZGVtb2dyYXBoaWMgZmFjdG9ycyBtYXkgbWFrZSBzcGVjaWZpYyBwb3B1bGF0aW9ucyBtb3JlIGF0IHJpc2sgZm9yIGFkdmVyc2UgaGVhbHRoIGVmZmVjdHMgZHVlIHRvIGFpciBwb2xsdXRpb24uIFNlZSB0aGlzIFthcnRpY2xlXShodHRwczovL3d3dy5uZWptLm9yZy9kb2kvZnVsbC8xMC4xMDU2L05FSk1vYTE3MDI3NDcpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgZGV0YWlscy4KClRoZSBtb25pdG9yIGRhdGEgdGhhdCB3ZSB3aWxsIHVzZSBpbiB0aGlzIGNhc2Ugc3R1ZHkgY29tZXMgZnJvbSBhIHN5c3RlbSBvZiBtb25pdG9ycyBpbiB3aGljaCByb3VnaGx5IDkwJSBhcmUgbG9jYXRlZCB3aXRoaW4gY2l0aWVzLiBUaHVzIHRoZXJlIGlzIGFuICoqZXF1aXR5IGlzc3VlKiogaW4gdGVybXMgb2YgY2FwdHVyaW5nIHRoZSBhaXIgcG9sbHV0aW9uIGxldmVscyBvZiBtb3JlIHJ1cmFsIGFyZWFzLiBUaGVyZWZvcmUsIHRvIGdldCBhIGJldHRlciBzZW5zZSBvZiB0aGUgcG9sbHV0aW9uIGV4cG9zdXJlcyBmb3IgdGhlIGluZGl2aWR1YWxzIGxpdmluZyBpbiB0aGVzZSBhcmVhcywgbWV0aG9kcyBsaWtlIG1hY2hpbmUgbGVhcm5pbmcgY2FuIGJlIHZlcnkgdXNlZnVsIHRvIGVzdGltYXRlIGFpciBwb2xsdXRpb24gbGV2ZWxzIGluICoqYXJlYXMgd2l0aCBsaXR0bGUgdG8gbm8gbW9uaXRvcmluZyoqLgoKSW5kZWVkLCBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZHMgYXJlIGluIGZhY3QgdXNlZCB0byBiZSBhYmxlIHRvIGVzdGltYXRlIGFpciBwb2xsdXRpb24gaW4gdGhlc2UgbG93IG1vbml0b3JpbmcgYXJlYXMgc28gdGhhdCB3ZSBjYW4gbWFrZSBhIG1hcCBsaWtlIHRoaXMgd2hlcmUgd2UgaGF2ZSBhbm51YWwgZXN0aW1hdGVzIGZvciBhbGwgb2YgdGhlIGNvbnRpZ3VvdXMgVVM6Cgo8cCBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI2MDAiIHNyYz0iaHR0cHM6Ly9hcmMtYW5nbGVyZmlzaC13YXNocG9zdC1wcm9kLXdhc2hwb3N0LnMzLmFtYXpvbmF3cy5jb20vcHVibGljL1NBV09FR0JYTVZHUTdBUzVQWjZVVU9YNkZZLnBuZyI+CjwvcD4KCgojIyMjIyBbW3NvdXJjZV1dKGh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vdXJsP3NhPWkmdXJsPWh0dHBzJTNBJTJGJTJGd3d3Lndhc2hpbmd0b25wb3N0LmNvbSUyRmJ1c2luZXNzJTJGMjAxOSUyRjEwJTJGMjMlMkZhaXItcG9sbHV0aW9uLWlzLWdldHRpbmctd29yc2UtZGF0YS1zaG93LW1vcmUtcGVvcGxlLWFyZS1keWluZyUyRiZwc2lnPUFPdlZhdzN2LVpEVEJQbkxQMk1ZdEtmM1VuZGomdXN0PTE1ODU3ODQ0NzkwNjgwMDAmc291cmNlPWltYWdlcyZjZD12ZmUmdmVkPTBDQUlRalJ4cUZ3b1RDUEN5bjlmeHhlZ0NGUUFBQUFBZEFBQUFBQkFkKQoKVGhpcyBpcyB3aGF0IHdlIGFpbSB0byBhY2hpZXZlIGluIHRoaXMgY2FzZSBzdHVkeS4KCiMjIyBMaW1pdGF0aW9ucwoKVGhlcmUgYXJlIHNvbWUgaW1wb3J0YW50IGNvbnNpZGVyYXRpb25zIHJlZ2FyZGluZyB0aGlzIGRhdGEgYW5hbHlzaXMgdG8ga2VlcCBpbiBtaW5kOiAKCjEpIFRoZSBkYXRhIGluIG91ciBhbmFseXNpcyBkb2VzIG5vdCBpbmNsdWRlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjb21wb3NpdGlvbiBvZiBwYXJ0aWN1bGF0ZSBtYXRlci4gRGlmZmVyZW50IHR5cGVzIG9mIHBhcnRpY3VsYXRlcyBtYXkgYmUgbW9yZSBiZW5pZ24gb3IgZGVsZXRlcmlvdXMgZm9yIGhlYWx0aCBvdXRjb21lcy4KCjIpIE91dGRvb3IgcG9sbHV0aW9uIGxldmVscyBhcmUgbm90IG5lY2Vzc2FyaWx5IGFuIGluZGljYXRpb24gb2Ygb2YgaW5kaXZpZHVhbCBleHBvc3VyZXMuIFBlb3BsZSBzcGVuZCBkaWZmZXJpbmcgYW1vdW50cyBvZiB0aW1lIGluZG9vcnMgYW5kIG91dGRvb3JzIGFuZCBhcmUgZXhwb3NlZCB0byBkaWZmZXJlbnQgcG9sbHV0aW9uIGxldmVscyBpbmRvb3JzLiBQZW9wbGUgYXJlIG5vdyBkZXZlbG9waW5nIHBlcnNvbmFsIG1vbml0b3Jpbmcgc3lzdGVtcyB0byB0cmFjayBhaXIgcG9sbHV0aW9uIGxldmVscyBvbiB0aGUgcGVyc29uYWwgbGV2ZWwuCgpPdXIgYW5hbHlzaXMgd2lsbCB1c2UgYW5udWFsIG1lYW4gZXN0aW1hdGVzLCBob3dldmVyIHBvbGx1dGlvbiBsZXZlbHMgY2FuIHZhcnkgZ3JlYXRseSBieSBzZWFzb24sIGRheSBhbmQgZXZlbiBob3VyLiBUaGVyZSBhcmUgZGF0YSBzb3VyY2VzIHRoYXQgaGF2ZSBmaW5lciBsZXZlbHMgb2YgdGVtcG9yYWwgZGF0YSwgaG93ZXZlciB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBsb25nIHRlcm0gZXhwb3N1cmVzLCBhcyB0aGVzZSBhcHBlYXIgdG8gYmUgdGhlIG1vc3QgaW5mbHVlbnRpYWwgZm9yIGhlYWx0aCBvdXRjb21lcywgc28gd2UgY2hvc2UgdG8gdXNlIGFubnVhbCBsZXZlbCBkYXRhLiAKCgojIyBXaGF0IGFyZSB0aGUgZGF0YT8KCkluIE1hY2hpbmUgTGVhcm5pbmcgZm9yIHByZWRpY3Rpb24sIHRoZXJlIGFyZSB0d28gbWFpbiB0eXBlcyBvZiB2YXJpYWJsZXM6CgoxKSBPdXRjb21lIHZhcmlhYmxlCjIpIFByZWRpY3RvciB2YXJpYWJsZXMKClRoZSAqKm91dGNvbWUgdmFyaWFibGUqKiBpcyB3aGF0IGFyZSB0cnlpbmcgdG8gKipwcmVkaWN0KiouIEluIGJ1aWxkaW5nIG91ciBtb2RlbCB3ZSBhY3R1YWxseSBoYXZlIHRoZSBvdXRjb21lIHZhcmlhYmxlIGRhdGEsIGJ1dCB3ZSB3YW50IHRvIHNlZSBob3cgd2VsbCBvdXIgcHJlZGljdG9yIHZhcmlhYmxlcyBjYW4gZXhwbGFpbiB0aGUgdmFyaWF0aW9uIGluIG91ciBvdXRjb21lIGRhdGEuIFRoaXMgZ2l2ZXMgdXMgYSBzZW5zZSBvZiBob3cgd2VsbCB3ZSBjYW4gdXNlIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUgZGF0YSB0byBwcmVkaWN0IG91ciBvdXRjb21lIHZhcmlhYmxlIGxldmVscyB3aGVuIHdlIGluIGZhY3QgZG8gbm90IGhhdmUgZGF0YSBhYm91dCB0aGUgb3V0Y29tZS4KCkFzIGEgc2ltcGxlciBleGFtcGxlLCBpbWFnaW5lIHRoYXQgd2UgaGF2ZSBkYXRhIGFib3V0IHRoZSBzYWxlcyBhbmQgY2hhcmFjdGVyaXN0aWNzIG9mIGNhcnMgZnJvbSBsYXN0IHllYXIgYW5kIHdlIHdhbnQgdG8gcHJlZGljdCB3aGljaCBjYXJzIG1pZ2h0IHNlbGwgd2VsbCB0aGlzIHllYXIuIFdlIGRvIG5vdCBoYXZlIHRoZSBzYWxlcyBkYXRhIHlldCBmb3IgdGhpcyB5ZWFyLCBidXQgd2UgZG8ga25vdyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIG91ciBjYXJzIGZvciB0aGlzIHllYXIuIFdlIGNhbiB1c2UgYSBtb2RlbCBvZiB0aGUgY2hhcmFjdGVyaXN0aWNzIHRoYXQgZXhwbGFpbmVkIHNhbGVzIGxhc3QgeWVhciB0byBlc3RpbWF0ZSB3aGF0IGNhcnMgbWlnaHQgc2VsbCB3ZWxsIHRoaXMgeWVhci4gSW4gdGhpcyBjYXNlLCBvdXIgb3V0Y29tZSB2YXJpYWJsZSBpcyB0aGUgc2FsZSBwZXJmb3JtYW5jZSBvZiB0aGUgY2Fycywgd2hpbGUgdGhlIGRpZmZlcmVudCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGNhcnMgbWFrZSB1cCBvdXIgcHJlZGljdG9yIG9yIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4KCkluIHRoaXMgY2FzZSBzdHVkeSwgd2Ugd2lsbCBldmFsdWF0ZSBhaXIgcG9sbHV0aW9uIG1vbml0b3IgZGF0YSBvZiBmaW5lIHBhcnRpY3VsYXRlIG1hdGVyIChQTX4yLjV+KSBpbiB0aGUgY29udGlndW91cyBVUyBmcm9tIDIwMDgsIGFzIHdlbGwgYXMgZGF0YSBhYm91dCBwb3B1bGF0aW9uIGRlbnNpdHksIHJvYWQgZGVuc2l0eSwgdXJiYW5pemF0aW9uIGxldmVscywgYW5kIE5BU0Egc2F0ZWxsaXRlIGRhdGEgdG8gZGV2ZWxvcCBtb2RlbHMgdG8gcHJlZGljdCBsb2NhbGl6ZWQgYWlyIHBvbGx1dGlvbiBsZXZlbHMuIAoKVGhlIG1vbml0b3IgZGF0YSB3aWxsIGJlIG91ciAqKm91dGNvbWUgdmFyaWFibGUqKi4gIFdlIHdhbnQgdG8gZGV0ZXJtaW5lIGlmIHdlIGNhbiAqKnByZWRpY3QqKiBhaXIgcG9sbHV0aW9uIGxldmVscyBiYXNlZCBvbiBvdGhlciB0eXBlcyBvZiBkYXRhLCBsaWtlIHJvYWQgZGVuc2l0eSBhbmQgcG9wdWxhdGlvbiBkZW5zaXR5IHRvIHNlZSBpZiB3ZSBjYW4gdXNlIHRoZXNlIGRhdGEgdG8gcHJlZGljdCBhaXIgcG9sbHV0aW9uIGluIGFyZWFzIHdoZXJlIHRoZXJlIGFyZSBubyBtb25pdG9ycy4gCgoKIyMjIE91ciBvdXRjb21lIHZhcmlhYmxlCgpUaGUgbW9uaXRvciBkYXRhIHRoYXQgd2Ugd2lsbCBiZSB1c2luZyBjb21lcyBmcm9tICoqW2dyYXZpbWV0cmljIG1vbml0b3JzXShodHRwczovL3B1YmxpY2xhYi5vcmcvd2lraS9maWx0ZXItcG0pe3RhcmdldD0iX2JsYW5rIn0qKiBvcGVyYXRlZCBieSB0aGUgVVMgW0VuaXZvcm5tZW50YWwgUHJvdGVjdGlvbiBBZ2VuY3kgKEVQQSldKGh0dHBzOi8vd3d3LmVwYS5nb3YvKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGVzZSBtb25pdG9ycyB1c2UgYSBmaWx0cmF0aW9uIHN5c3RlbSB0byBzcGVjaWZpY2FsbHkgY2FwdHVyZSBmaW5lIHBhcnRpY3VsYXRlIG1hdHRlci4gVGhlIHdlaWdodCBvZiB0aGlzIG1hdHRlciBpcyBtYW51YWxseSBtZWFzdXJlZCBkYWlseSBvciB3ZWVrbHkuIFNlZSBbaGVyZV0oaHR0cHM6Ly93d3czLmVwYS5nb3YvdHRuYW10aTEvZmlsZXMvYW1iaWVudC9wbTI1L3NwZWMvUlRJR3Jhdk1hc3NTT1BGSU5BTC5wZGYpe3RhcmdldD0iX2JsYW5rIn0gZm9yIHRoZSBFUEEgc3RhbmRhcmQgb3BlcmF0aW5nIHByb2NlZHVyZSBmb3IgUE0gZ3JhdmltZXRyaWMgYW5hbHlzaXMgaW4gMjAwOC4KCgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSIxNTBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsImZpbHRlci5wbmciKSkKYGBgCgojIyMjIyBbW3NvdXJjZV0oaHR0cHM6Ly9wdWJsaWNsYWIub3JnL3dpa2kvZmlsdGVyLXBtKV0KCkhlcmUgaXMgYW4gaW1hZ2Ugb2Ygd2hhdCB0aGUgZ3JhdmltZXRyaWMgbW9uaXRvcnMgbG9vayBsaWtlOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSIxMDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsIm1vbml0b3IucG5nIikpCmBgYAoKCkdyYXZpbWV0cmljIGFuYWx5c2lzIGlzIGFsc28gdXNlZCBmb3IgW2VtaXNzaW9uIHRlc3RpbmddKGh0dHBzOi8vd3d3Lm10LmNvbS91cy9lbi9ob21lL2FwcGxpY2F0aW9ucy9MYWJvcmF0b3J5X3dlaWdoaW5nL2VtaXNzaW9ucy10ZXN0aW5nLXBhcnRpY3VsYXRlLW1hdHRlci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGUgc2FtZSBpZGVhIGFwcGxpZXM6IGEgZnJlc2ggZmlsdGVyIGlzIGFwcGxpZWQgYW5kIHRoZSBkZXNpcmVkIGFtb3VudCBvZiB0aW1lIHBhc3NlcywgdGhlbiB0aGUgZmlsdGVyIGlzIHJlbW92ZWQgYW5kIHdlaWdoZWQuIAoKVGhlcmUgYXJlIFtvdGhlciBtb25pdG9yaW5nIHN5c3RlbXNdKGh0dHBzOi8vd3d3LnNlbnNpcmlvbi5jb20vZW4vYWJvdXQtdXMvbmV3c3Jvb20vc2Vuc2lyaW9uLXNwZWNpYWxpc3QtYXJ0aWNsZXMvcGFydGljdWxhdGUtbWF0dGVyLXNlbnNpbmctZm9yLWFpci1xdWFsaXR5LW1lYXN1cmVtZW50cy8pe3RhcmdldD0iX2JsYW5rIn0gdGhhdCBjYW4gcHJvdmlkZSBob3VybHkgbWVhc3VyZW1lbnRzLCBidXQgd2Ugd2lsbCBub3QgYmUgdXNpbmcgZGF0YSBmcm9tIHRoZXNlIG1vbml0b3JzIGluIG91ciBhbmFseXNpcy4gR3JhdmltZXRyaWMgYW5hbHlzaXMgaXMgY29uc2lkZXJlZCB0byBiZSBhbW9uZyB0aGUgbW9zdCBhY2N1cmF0ZSBtZXRob2RzLgoKSW4gb3VyIGNzdiwgdGhlICoqdmFsdWUqKiBjb2x1bW4gaW5kaWNhdGVzIHRoZSBQTX4yLjV+IG1vbml0b3IgYXZlcmFnZSBmb3IgMjAwOCBpbiBtYXNzIG9mIGZpbmUgcGFydGljbGVzL3ZvbHVtZSBvZiBhaXIgZm9yIDg3NiBncmF2aW1ldHJpYyBtb25pdG9ycy4gVGhlIHVuaXRzIGFyZSBtaWNybyBncmFtcyBvZiBmaW5lIHBhcnRpY3VsYXRlIG1hdGVyIChQTSkgdGhhdCBpcyBsZXNzIHRoYW4gMi41IG1pY3JvbWV0ZXJzIGluIGRpYW1ldGVyIHBlciBjdWJpYyBtZXRlciBvZiBhaXIgLSBtYXNzIGNvbmNlbnRyYXRpb24gKHVnL21eM14pLiAgUmVjYWxsIHRoZSB0aGUgV0hPIGV4cG9zdXJlICBndWlkZWxpbmUgaXMgPCAxMCB1Zy9tXjNeIG9uIGF2ZXJhZ2UgYW5udWFsbHkgZm9yIFBNfjIuNX4uCgojIyMgT3VyIHByZWRpY3RvciB2YXJpYWJsZXMKClRoZXJlIGFyZSA0OCBwcmVkaWN0b3IgdmFyaWFibGVzIHdpdGggdmFsdWVzIGZvciBlYWNoIG9mIHRoZSA4NzYgbW9uaXRvcnMgaW5jbHVkZWQgaW4gb3VyIG91dGNvbWUgdmFyaWFibGUuIFRoZSBkYXRhIGNvbWVzIGZyb20gdGhlIFVTIFtFbml2b3JubWVudGFsIFByb3RlY3Rpb24gQWdlbmN5IChFUEEpXShodHRwczovL3d3dy5lcGEuZ292Lyl7dGFyZ2V0PSJfYmxhbmsifSwgdGhlIFtOYXRpb25hbCBBZXJvbmF1dGljcyBhbmQgU3BhY2UgQWRtaW5pc3RyYXRpb24gKE5BU0EpXShodHRwczovL3d3dy5uYXNhLmdvdi8pe3RhcmdldD0iX2JsYW5rIn0sIHRoZSBVUyBbQ2Vuc3VzXShodHRwczovL3d3dy5jZW5zdXMuZ292L2Fib3V0L3doYXQvY2Vuc3VzLWF0LWEtZ2xhbmNlLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0sIGFuZCB0aGUgW05hdGlvbmFsIENlbnRlciBmb3IgSGVhbHRoIFN0YXRpc3RpY3MgKE5DSFMpXShodHRwczovL3d3dy5jZGMuZ292L25jaHMvYWJvdXQvaW5kZXguaHRtKXt0YXJnZXQ9Il9ibGFuayJ9LgoKVmFyaWFibGUgICB8IERldGFpbHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQoqKmlkKiogIHwgTW9uaXRvciBudW1iZXIgIDxicj4gLS0gdGhlIGNvdW50eSBudW1iZXIgaXMgaW5kaWNhdGVkIGJlZm9yZSB0aGUgZGVjaW1hbCA8YnI+IC0tIHRoZSBtb25pdG9yIG51bWJlciBpcyBpbmRpY2F0ZWQgYWZ0ZXIgdGhlIGRlY2ltYWwgPGJyPiAgKipFeGFtcGxlKio6IDEwNzMuMDAyMyAgaXMgSmVmZmVyc29uIGNvdW50eSAoMTA3MykgYW5kIC4wMDIzIG9uZSBvZiA4IG1vbml0b3JzIAoqKmZpcHMqKiB8IEZlZGVyYWwgaW5mb3JtYXRpb24gcHJvY2Vzc2luZyBzdGFuZGFyZCBudW1iZXIgZm9yIHRoZSBjb3VudHkgd2hlcmUgdGhlIG1vbml0b3IgaXMgbG9jYXRlZCA8YnI+IC0tIDUgZGlnaXQgaWQgY29kZSBmb3IgY291bnRpZXMgKHplcm8gaXMgb2Z0ZW4gdGhlIGZpcnN0IHZhbHVlIGFuZCBzb21ldGltZXMgaXMgbm90IHNob3duKSA8YnI+IC0tIHRoZSBmaXJzdCAyIG51bWJlcnMgaW5kaWNhdGUgdGhlIHN0YXRlIDxicj4gLS0gdGhlIGxhc3QgdGhyZWUgbnVtYmVycyBpbmRpY2F0ZSB0aGUgY291bnR5IDxicj4gICoqRXhhbXBsZSoqOiBBbGFiYW1hJ3Mgc3RhdGUgY29kZSBpcyAwMSBiZWNhdXNlIGl0IGlzIGZpcnN0IGFscGhhYmV0aWNhbGx5IDxicj4gKG5vdGU6IEFsYXNrYSBhbmQgSGF3YWlpIGFyZSBub3QgaW5jbHVkZWQgYmVjYXVzZSB0aGV5IGFyZSBub3QgcGFydCBvZiB0aGUgY29udGlndW91cyBVUykgIAoqKkxhdCoqIHwgTGF0aXR1ZGUgb2YgdGhlIG1vbml0b3IgaW4gZGVncmVlcyAgCioqTG9uKiogfCBMb25naXR1ZGUgb2YgdGhlIG1vbml0b3IgaW4gZGVncmVlcyAgCioqc3RhdGUqKiB8IFN0YXRlIHdoZXJlIHRoZSBtb25pdG9yIGlzIGxvY2F0ZWQKKipjb3VudHkqKiB8IENvdW50eSB3aGVyZSB0aGUgbW9uaXRvciBpcyBsb2NhdGVkCioqY2l0eSoqIHwgQ2l0eSB3aGVyZSB0aGUgbW9uaXRvciBpcyBsb2NhdGVkCioqQ01BUSoqICB8IEVzdGltYXRlZCB2YWx1ZXMgb2YgYWlyIHBvbGx1dGlvbiBmcm9tIGEgY29tcHV0YXRpb25hbCBtb2RlbCBjYWxsZWQgWyoqQ29tbXVuaXR5IE11bHRpc2NhbGUgQWlyIFF1YWxpdHkgKENNQVEpKipdKGh0dHBzOi8vd3d3LmVwYS5nb3YvY21hcSl7dGFyZ2V0PSJfYmxhbmsifSA8YnI+IC0tICBBIG1vbml0b3Jpbmcgc3lzdGVtIHRoYXQgc2ltdWxhdGVzIHRoZSBwaHlzaWNzIG9mIHRoZSBhdG1vc3BoZXJlIHVzaW5nIGNoZW1pc3RyeSBhbmQgd2VhdGhlciBkYXRhIHRvIHByZWRpY3QgdGhlIGFpciBwb2xsdXRpb24gPGJyPiAtLSAqKipEb2VzIG5vdCB1c2UgYW55IG9mIHRoZSBQTX4yLjV+IGdyYXZpbWV0cmljIG1vbml0b3JpbmcgZGF0YS4qKiogKFRoZXJlIGlzIGEgdmVyc2lvbiB0aGF0IGRvZXMgdXNlIHRoZSBncmF2aW1ldHJpYyBtb25pdG9yaW5nIGRhdGEsIGJ1dCBub3QgdGhpcyBvbmUhKSA8YnI+IC0tIERhdGEgZnJvbSB0aGUgRVBBCioqemN0YSoqIHwgW1ppcCBDb2RlIFRhYnVsYXRpb24gQXJlYV0oaHR0cHM6Ly93d3cyLmNlbnN1cy5nb3YvZ2VvL3BkZnMvZWR1Y2F0aW9uL2Jyb2NodXJlcy9aQ1RBcy5wZGYpe3RhcmdldD0iX2JsYW5rIn0gd2hlcmUgdGhlIG1vbml0b3IgaXMgbG9jYXRlZCA8YnI+IC0tIFBvc3RhbCBaaXAgY29kZXMgYXJlIGNvbnZlcnRlZCBpbnRvICJnZW5lcmFsaXplZCBhcmVhbCByZXByZXNlbnRhdGlvbnMiIHRoYXQgYXJlIG5vbi1vdmVybGFwcGluZyAgPGJyPiAtLSBEYXRhIGZyb20gdGhlIDIwMTAgQ2Vuc3VzICAKKip6Y3RhX2FyZWEqKiB8IExhbmQgYXJlYSBvZiB0aGUgemlwIGNvZGUgYXJlYSBpbiBtZXRlcnMgc3F1YXJlZCAgPGJyPiAtLSBEYXRhIGZyb20gdGhlIDIwMTAgQ2Vuc3VzICAKKip6Y3RhX3BvcCoqIHwgUG9wdWxhdGlvbiBpbiB0aGUgemlwIGNvZGUgYXJlYSAgPGJyPiAtLSBEYXRhIGZyb20gdGhlIDIwMTAgQ2Vuc3VzICAKKippbXBfYTUwMCoqIHwgSW1wZXJ2aW91cyBzdXJmYWNlIG1lYXN1cmUgPGJyPiAtLSBXaXRoaW4gYSBjaXJjbGUgd2l0aCBhIHJhZGl1cyBvZiA1MDAgbWV0ZXJzIGFyb3VuZCB0aGUgbW9uaXRvciA8YnI+IC0tIEltcGVydmlvdXMgc3VyZmFjZSBhcmUgcm9hZHMsIGNvbmNyZXRlLCBwYXJraW5nIGxvdHMsIGJ1aWxkaW5ncyA8YnI+IC0tIFRoaXMgaXMgYSBtZWFzdXJlIG9mIGRldmVsb3BtZW50IAoqKmltcF9hMTAwMCoqIHwgSW1wZXJ2aW91cyBzdXJmYWNlIG1lYXN1cmUgPGJyPiAtLSAgV2l0aGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTAwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yCioqaW1wX2E1MDAwKiogfCBJbXBlcnZpb3VzIHN1cmZhY2UgbWVhc3VyZSA8YnI+IC0tICBXaXRoaW4gYSBjaXJjbGUgd2l0aCBhIHJhZGl1cyBvZiA1MDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgIAoqKmltcF9hMTAwMDAqKiB8IEltcGVydmlvdXMgc3VyZmFjZSBtZWFzdXJlIDxicj4gLS0gIFdpdGhpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDEwMDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgICAKKippbXBfYTE1MDAwKiogfCBJbXBlcnZpb3VzIHN1cmZhY2UgbWVhc3VyZSA8YnI+IC0tICBXaXRoaW4gYSBjaXJjbGUgd2l0aCBhIHJhZGl1cyBvZiAxNTAwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yICAKKipjb3VudHlfYXJlYSoqIHwgTGFuZCBhcmVhIG9mIHRoZSBjb3VudHkgb2YgdGhlIG1vbml0b3IgaW4gbWV0ZXJzIHNxdWFyZWQgIAoqKmNvdW50eV9wb3AqKiB8IFBvcHVsYXRpb24gb2YgdGhlIGNvdW50eSBvZiB0aGUgbW9uaXRvciAgCioqTG9nX2Rpc3RfdG9fcHJpc2VjKiogfCBMb2cgKE5hdHVyYWwgbG9nKSBkaXN0YW5jZSB0byBhIHByaW1hcnkgb3Igc2Vjb25kYXJ5IHJvYWQgZnJvbSB0aGUgbW9uaXRvciA8YnI+IC0tIEhpZ2h3YXkgb3IgbWFqb3Igcm9hZCAgCioqbG9nX3ByaV9sZW5ndGhfNTAwMCoqIHwgQ291bnQgb2YgcHJpbWFyeSByb2FkIGxlbmd0aCBpbiBtZXRlcnMgaW4gYSBjaXJjbGUgd2l0aCBhIHJhZGl1cyBvZiA1MDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSA8YnI+IC0tIEhpZ2h3YXlzIG9ubHkgIAoqKmxvZ19wcmlfbGVuZ3RoXzEwMDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IHJvYWQgbGVuZ3RoIGluIG1ldGVycyBpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDEwMDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSA8YnI+IC0tIEhpZ2h3YXlzIG9ubHkgIAoqKmxvZ19wcmlfbGVuZ3RoXzE1MDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IHJvYWQgbGVuZ3RoIGluIG1ldGVycyBpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDE1MDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSA8YnI+IC0tIEhpZ2h3YXlzIG9ubHkgIAoqKmxvZ19wcmlfbGVuZ3RoXzI1MDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IHJvYWQgbGVuZ3RoIGluIG1ldGVycyBpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDI1MDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSA8YnI+IC0tIEhpZ2h3YXlzIG9ubHkgIAoqKmxvZ19wcmlzZWNfbGVuZ3RoXzUwMCoqIHwgQ291bnQgb2YgcHJpbWFyeSBhbmQgc2Vjb25kYXJ5IHJvYWQgbGVuZ3RoIGluIG1ldGVycyBpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDUwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yIChOYXR1cmFsIGxvZykgIDxicj4gLS0gSGlnaHdheSBhbmQgc2Vjb25kYXJ5IHJvYWRzICAKKipsb2dfcHJpc2VjX2xlbmd0aF8xMDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgcm9hZCBsZW5ndGggaW4gbWV0ZXJzIGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTAwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yIChOYXR1cmFsIGxvZykgIDxicj4gLS0gSGlnaHdheSBhbmQgc2Vjb25kYXJ5IHJvYWRzICAKKipsb2dfcHJpc2VjX2xlbmd0aF81MDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgcm9hZCBsZW5ndGggaW4gbWV0ZXJzIGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgNTAwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yIChOYXR1cmFsIGxvZykgIDxicj4gLS0gSGlnaHdheSBhbmQgc2Vjb25kYXJ5IHJvYWRzICAKKipsb2dfcHJpc2VjX2xlbmd0aF8xMDAwMCoqIHwgQ291bnQgb2YgcHJpbWFyeSBhbmQgc2Vjb25kYXJ5IHJvYWQgbGVuZ3RoIGluIG1ldGVycyBpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDEwMDAwIG1ldGVycyBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSAgPGJyPiAtLSBIaWdod2F5IGFuZCBzZWNvbmRhcnkgcm9hZHMgIAoqKmxvZ19wcmlzZWNfbGVuZ3RoXzE1MDAwKiogfCBDb3VudCBvZiBwcmltYXJ5IGFuZCBzZWNvbmRhcnkgcm9hZCBsZW5ndGggaW4gbWV0ZXJzIGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTUwMDAgbWV0ZXJzIGFyb3VuZCB0aGUgbW9uaXRvciAoTmF0dXJhbCBsb2cpICA8YnI+IC0tIEhpZ2h3YXkgYW5kIHNlY29uZGFyeSByb2FkcyAgCioqbG9nX3ByaXNlY19sZW5ndGhfMjUwMDAqKiB8IENvdW50IG9mIHByaW1hcnkgYW5kIHNlY29uZGFyeSByb2FkIGxlbmd0aCBpbiBtZXRlcnMgaW4gYSBjaXJjbGUgd2l0aCBhIHJhZGl1cyBvZiAyNTAwMCBtZXRlcnMgYXJvdW5kIHRoZSBtb25pdG9yIChOYXR1cmFsIGxvZykgIDxicj4gLS0gSGlnaHdheSBhbmQgc2Vjb25kYXJ5IHJvYWRzICAgICAgCioqbG9nX25laV8yMDA4X3BtMjVfc3VtXzEwMDAwKiogfCBUb25zIG9mIGVtaXNzaW9ucyBmcm9tIG1ham9yIHNvdXJjZXMgZGF0YSBiYXNlIChhbm51YWwgZGF0YSkgc3VtIG9mIGFsbCBzb3VyY2VzIHdpdGhpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDEwMDAwIG1ldGVycyBvZiBkaXN0YW5jZSBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSAgICAKKipsb2dfbmVpXzIwMDhfcG0yNV9zdW1fMTUwMDAqKiB8IFRvbnMgb2YgZW1pc3Npb25zIGZyb20gbWFqb3Igc291cmNlcyBkYXRhIGJhc2UgKGFubnVhbCBkYXRhKSBzdW0gb2YgYWxsIHNvdXJjZXMgd2l0aGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTUwMDAgbWV0ZXJzIG9mIGRpc3RhbmNlIGFyb3VuZCB0aGUgbW9uaXRvciAoTmF0dXJhbCBsb2cpICAgICAKKipsb2dfbmVpXzIwMDhfcG0yNV9zdW1fMjUwMDAqKiB8IFRvbnMgb2YgZW1pc3Npb25zIGZyb20gbWFqb3Igc291cmNlcyBkYXRhIGJhc2UgKGFubnVhbCBkYXRhKSBzdW0gb2YgYWxsIHNvdXJjZXMgd2l0aGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMjUwMDAgbWV0ZXJzIG9mIGRpc3RhbmNlIGFyb3VuZCB0aGUgbW9uaXRvciAoTmF0dXJhbCBsb2cpICAgICAKKipsb2dfbmVpXzIwMDhfcG0xMF9zdW1fMTAwMDAqKiB8IFRvbnMgb2YgZW1pc3Npb25zIGZyb20gbWFqb3Igc291cmNlcyBkYXRhIGJhc2UgKGFubnVhbCBkYXRhKSBzdW0gb2YgYWxsIHNvdXJjZXMgd2l0aGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTAwMDAgbWV0ZXJzIG9mIGRpc3RhbmNlIGFyb3VuZCB0aGUgbW9uaXRvciAoTmF0dXJhbCBsb2cpICAgICAgCioqbG9nX25laV8yMDA4X3BtMTBfc3VtXzE1MDAwKip8IFRvbnMgb2YgZW1pc3Npb25zIGZyb20gbWFqb3Igc291cmNlcyBkYXRhIGJhc2UgKGFubnVhbCBkYXRhKSBzdW0gb2YgYWxsIHNvdXJjZXMgd2l0aGluIGEgY2lyY2xlIHdpdGggYSByYWRpdXMgb2YgMTUwMDAgbWV0ZXJzIG9mIGRpc3RhbmNlIGFyb3VuZCB0aGUgbW9uaXRvciAoTmF0dXJhbCBsb2cpICAgICAgCioqbG9nX25laV8yMDA4X3BtMTBfc3VtXzI1MDAwKiogfCBUb25zIG9mIGVtaXNzaW9ucyBmcm9tIG1ham9yIHNvdXJjZXMgZGF0YSBiYXNlIChhbm51YWwgZGF0YSkgc3VtIG9mIGFsbCBzb3VyY2VzIHdpdGhpbiBhIGNpcmNsZSB3aXRoIGEgcmFkaXVzIG9mIDI1MDAwIG1ldGVycyBvZiBkaXN0YW5jZSBhcm91bmQgdGhlIG1vbml0b3IgKE5hdHVyYWwgbG9nKSAgICAgIAoqKnBvcGRlbnNfY291bnR5KiogfCBQb3B1bGF0aW9uIGRlbnNpdHkgKG51bWJlciBvZiBwZW9wbGUgcGVyIGtpbG9tZXRlciBzcXVhcmVkIGFyZWEgb2YgdGhlIGNvdW50eSkKKipwb3BkZW5zX3pjdGEqKiB8IFBvcHVsYXRpb24gZGVuc2l0eSAobnVtYmVyIG9mIHBlb3BsZSBwZXIga2lsb21ldGVyIHNxdWFyZWQgYXJlYSBvZiB6Y3RhKQoqKm5vaHMqKiB8IFBlcmNlbnRhZ2Ugb2YgcGVvcGxlIGluIHpjdGEgYXJlYSB3aGVyZSB0aGUgbW9uaXRvciBpcyB0aGF0ICoqZG8gbm90IGhhdmUgYSBoaWdoIHNjaG9vbCBkZWdyZWUqKiA8YnI+IC0tIERhdGEgZnJvbSB0aGUgQ2Vuc3VzCioqc29tZWhzKiogfCBQZXJjZW50YWdlIG9mIHBlb3BsZSBpbiB6Y3RhIGFyZWEgd2hlcmUgdGhlIG1vbml0b3Igd2hvc2UgaGlnaGVzdCBmb3JtYWwgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCB3YXMgKipzb21lIGhpZ2ggc2Nob29sIGVkdWNhdGlvbioqIDxicj4gLS0gRGF0YSBmcm9tIHRoZSBDZW5zdXMKKipocyoqIHwgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIHdob3NlIGhpZ2hlc3QgZm9ybWFsIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgd2FzIGNvbXBsZXRpbmcgYSAqKmhpZ2ggc2Nob29sIGRlZ3JlZSoqIDxicj4gLS0gRGF0YSBmcm9tIHRoZSBDZW5zdXMgIAoqKnNvbWVjb2xsZWdlKiogfCBQZXJjZW50YWdlIG9mIHBlb3BsZSBpbiB6Y3RhIGFyZWEgd2hlcmUgdGhlIG1vbml0b3Igd2hvc2UgaGlnaGVzdCBmb3JtYWwgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCB3YXMgY29tcGxldGluZyAqKnNvbWUgY29sbGVnZSBlZHVjYXRpb24qKiA8YnI+IC0tIERhdGEgZnJvbSB0aGUgQ2Vuc3VzIAoqKmFzc29jaWF0ZSoqIHwgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIHdob3NlIGhpZ2hlc3QgZm9ybWFsIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgd2FzIGNvbXBsZXRpbmcgYW4gKiphc3NvY2lhdGUgZGVncmVlKiogPGJyPiAtLSBEYXRhIGZyb20gdGhlIENlbnN1cyAKKipiYWNoZWxvcioqIHwgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIHdob3NlIGhpZ2hlc3QgZm9ybWFsIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgd2FzIGEgKipiYWNoZWxvcidzIGRlZ3JlZSoqIDxicj4gLS0gRGF0YSBmcm9tIHRoZSBDZW5zdXMgCioqZ3JhZCoqIHwgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIHdob3NlIGhpZ2hlc3QgZm9ybWFsIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgd2FzIGEgKipncmFkdWF0ZSBkZWdyZWUqKiA8YnI+IC0tIERhdGEgZnJvbSB0aGUgQ2Vuc3VzIAoqKnBvdioqIHwgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIGlzIHRoYXQgbGl2ZWQgaW4gWyoqcG92ZXJ0eSoqXShodHRwczovL2FzcGUuaGhzLmdvdi8yMDA4LWhocy1wb3ZlcnR5LWd1aWRlbGluZXMpIGluIDIwMDggLSBvciB3b3VsZCBpdCBoYXZlIGJlZW4gMjAwNyBndWlkZWxpbmVzPz9odHRwczovL2FzcGUuaGhzLmdvdi8yMDA3LWhocy1wb3ZlcnR5LWd1aWRlbGluZXMgPGJyPiAtLSBEYXRhIGZyb20gdGhlIENlbnN1cyAgCioqaHNfb3JsZXNzKiogfCAgUGVyY2VudGFnZSBvZiBwZW9wbGUgaW4gemN0YSBhcmVhIHdoZXJlIHRoZSBtb25pdG9yIHdob3NlIGhpZ2hlc3QgZm9ybWFsIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgd2FzIGEgKipoaWdoIHNjaG9vbCBkZWdyZWUgb3IgbGVzcyoqIChzdW0gb2Ygbm9ocywgc29tZWhzLCBhbmQgaHMpICAKKip1cmMyMDEzKiogfCBbMjAxMyBVcmJhbi1ydXJhbCBjbGFzc2lmaWNhdGlvbl0oaHR0cHM6Ly93d3cuY2RjLmdvdi9uY2hzL2RhdGEvc2VyaWVzL3NyXzAyL3NyMDJfMTY2LnBkZil7dGFyZ2V0PSJfYmxhbmsifSBvZiB0aGUgY291bnR5IHdoZXJlIHRoZSBtb25pdG9yIGlzIGxvY2F0ZWQgPGJyPiAtLSA2IGNhdGVnb3J5IHZhcmlhYmxlIC0gMSBpcyB0b3RhbGx5IHVyYmFuIDYgaXMgY29tcGxldGVseSBydXJhbCA8YnI+ICAtLSBEYXRhIGZyb20gdGhlIE5hdGlvbmFsIENlbnRlciBmb3IgSGVhbHRoIFN0YXRpc3RpY3NdKGh0dHBzOi8vd3d3LmNkYy5nb3YvbmNocy9pbmRleC5odG0pe3RhcmdldD0iX2JsYW5rIn0gICAgIAoqKnVyYzIwMDYqKiB8IFsyMDA2IFVyYmFuLXJ1cmFsIGNsYXNzaWZpY2F0aW9uXShodHRwczovL3d3dy5jZGMuZ292L25jaHMvZGF0YS9zZXJpZXMvc3JfMDIvc3IwMl8xNTQucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IG9mIHRoZSBjb3VudHkgd2hlcmUgdGhlIG1vbml0b3IgaXMgbG9jYXRlZCA8YnI+IC0tIDYgY2F0ZWdvcnkgdmFyaWFibGUgLSAxIGlzIHRvdGFsbHkgdXJiYW4gNiBpcyBjb21wbGV0ZWx5IHJ1cmFsIDxicj4gLS0gRGF0YSBmcm9tIHRoZSBbTmF0aW9uYWwgQ2VudGVyIGZvciBIZWFsdGggU3RhdGlzdGljc10oaHR0cHM6Ly93d3cuY2RjLmdvdi9uY2hzL2luZGV4Lmh0bSl7dGFyZ2V0PSJfYmxhbmsifSAgICAgCioqYW9kKiogfCBBZXJvc29sIE9wdGljYWwgRGVwdGggbWVhc3VyZW1lbnQgZnJvbSBhIE5BU0Egc2F0ZWxsaXRlIDxicj4gLS0gYmFzZWQgb24gdGhlIGRlZnJhY3Rpb24gb2YgYSBsYXNlciA8YnI+IC0tIHVzZWQgYXMgYSBwcm94eSBvZiBwYXJ0aWN1bGF0ZSBwb2xsdXRpb24gPGJyPiAtLSB1bml0LWxlc3MgLSBoaWdoZXIgdmFsdWUgaW5kaWNhdGVzIG1vcmUgcG9sbHV0aW9uIDxicj4gLS0gRGF0YSBmcm9tIE5BU0EgIAoKTWFueSBvZiB0aGVzZSBwcmVkaWN0b3IgdmFyaWFibGVzIGhhdmUgdG8gZG8gd2l0aCB0aGUgY2lyY3VsYXIgYXJlYSBhcm91bmQgdGhlIG1vbml0b3IgY2FsbGVkIHRoZSAiYnVmZmVyIi4gVGhlc2UgYXJlIGlsbHVzdHJhdGVkIGluIHRoZSBmb2xsb3dpbmcgZmlndXJlOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjgwMHB4Iix9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJyZWdyZXNzaW9uLnBuZyIpKQpgYGAKCiMjIyMjIFtbc291cmNlXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3B1Ym1lZC8xNTI5MjkwNildCgoKCiMjIERhdGEgSW1wb3J0CgpXZSBoYXZlIG9uZSBDU1YgZmlsZSB0aGF0IGNvbnRhaW5zIGJvdGggb3VyIHNpbmdsZSAqKm91dGNvbWUgdmFyaWFibGUqKiBhbmQgYWxsIG9mIG91ciAqKnByZWRpY3RvciB2YXJpYWJsZXMqKi4KCkxldCdzIGltcG9ydCBvdXIgZGF0YSBpbnRvIFIgbm93IHNvIHRoYXQgd2UgY2FuIGV4cGxvcmUgdGhlIGRhdGEgZnVydGhlci4gV2Ugd2lsbCBjYWxsIG91ciBkYXRhIG9iamVjdCBgcG1gIGZvciBwYXJ0aWN1bGF0ZSBtYXR0ZXIuCgpgYGB7cn0KcG0gPC1yZWFkcjo6cmVhZF9jc3YoaGVyZSgiZG9jcyIsICJwbTI1X2RhdGEuY3N2IikpCmBgYAoKIyMgRGF0YSBFeHBsb3JhdGlvbiBhbmQgV3JhbmdsaW5nCgpUaGUgZmlyc3Qgc3RlcCBpbiBwZXJmb3JtaW5nIGEgbWFjaGluZSBsZWFybmluZyBhbmFseXNpcyBpcyB0byBleHBsb3JlIHRoZSBkYXRhIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSB2YXJpYWJsZXMgIGluY2x1ZGVkIGluIHRoZSBkYXRhLCBhcyB3ZSBtYXkgbGVhcm4gYWJvdXQgaW1wb3J0YW50IGRldGFpbHMgYWJvdXQgdGhlIGRhdGEgdGhhdCB3ZSBzaG91bGQga2VlcCBpbiBtaW5kIGFzIHdlIHRyeSB0byBwcmVkaWN0IG91ciBvdXRjb21lIHZhcmlhYmxlLgoKRmlyc3QgbGV0J3MganVzdCBnZXQgYSBnZW5lcmFsIHNlbnNlIG9mIG91ciBkYXRhLiBXZSBjYW4gZG8gdGhhdCB1c2luZyB0aGUgYGdsaW1wc2UoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSAoaXQgaXMgYWxzbyBpbiB0aGUgYHRpYmJsZWAgcGFja2FnZSkuCgpXZSB3aWxsIGFsc28gdXNlIHRoZSBgJT4lYCBwaXBlIHdoaWNoIGNhbiBiZSB1c2VkIHRvIGRlZmluZSB0aGUgaW5wdXQgZm9yIGxhdGVyIHNlcXVlbnRpYWwgc3RlcHMuIFRoaXMgd2lsbCBtYWtlIG1vcmUgc2Vuc2Ugd2hlbiB3ZSBoYXZlIG11bHRpcGxlIHNlcXVlbnRpYWwgc3RlcHMgdXNpbmcgdGhlIHNhbWUgZGF0YSBvYmplY3QuIFRvIHVzZSB0aGUgcGlwZSBub3RhdGlvbiB3ZSBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgZHBseXIgYXMgd2VsbC4KCkZvciBleGFtcGxlIGhlcmUgd2Ugd2lsbCBmaXJzdCBncmFiIHRoZSBgcG1gIGRhdGEgb2JqZWN0LCB0aGVuIHdlIHVzZSB0aGUgYGdsaW1wc2UoKWAgZnVuY3Rpb24gb24gaXQgYmFzZWQgb24gdGhlIHBpcGUgbm90YXRpb24uCgoKYGBge3J9CnBtICU+JQogIGRwbHlyOjpnbGltcHNlKCkKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIDg3NiBtb25pdG9ycyBhbmQgdGhhdCB3ZSBoYXZlIDUwIHRvdGFsIHZhcmlhYmxlcyAtIG9uZSBvZiB3aGljaCBpcyB0aGUgb3V0Y29tZS4gSW4gdGhpcyBjYXNlIG91ciBvdXRjb21lIHZhcmlhYmxlIGlzIGNhbGxlZCBgdmFsdWVgLiAKCk5vdGljZSB0aGF0IHNvbWUgb2YgdGhlIHZhcmlhYmxlcyB0aGF0IHdlIHdvdWxkIHRoaW5rIG9mIGFzIGZhY3RvcnMgKGNhdGVnb3JpY2FsKSBhcmUgY3VycmVudGx5IG9mIGNsYXNzIGRvdWJsZSBhcyBpbmRpY2F0ZWQgYnkgdGhlIGA8ZGJsPmAganVzdCB0byB0aGUgcmlnaHQgb2YgdGhlIGNvbHVtbiBuYW1lcy92YXJpYWJsZSBuYW1lcyBpbiB0aGUgYGdsaW1wc2UoKWAgb3V0cHV0LiBGb3IgZXhhbXBsZSB0aGUgbW9uaXRvciBJRCAoaWQpLCB0aGUgRmVkZXJhbCBJbmZvcm1hdGlvbiBQcm9jZXNzaW5nIFN0YW5kYXJkIG51bWJlciBmb3IgdGhlIGNvdW50eSB3aGVyZSB0aGUgbW9uaXRvciB3YXMgbG9jYXRlZCAoZmlwcyksIGFzIHdlbGwgYXMgdGhlIHpjdGEKCkxldCdzIGNvbnZlcnQgdGhlc2UgdmFyaWFibGVzIGludG8gZmFjdG9ycy4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBtdXRhdGVfYXQoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSBhbmQgdGhlIGBhcy5mYWN0b3IoKWAgYmFzZSBmdW5jdGlvbi4gCgpJbiB0aGlzIGNhc2Ugd2UgYXJlIGFsc28gdXNpbmcgdGhlIG1hZ3JpdHRyIGFzc2lnbm1lbnQgcGlwZSBvciBkb3VibGUgcGlwZSB0aGF0IGxvb2tzIGxpa2UgdGhpcyBgJTw+JWAgb2YgdGhlIGBtYWdyaXR0cmAgcGFja2FnZS4gVGhpcyBhbGxvd3MgdXMgdXNlIHRoZSBgcG1gIGRhdGEgYXMgaW5wdXQgYnV0IGFsc28gcmVhc3NpZ24gdGhlIG91dHB1dCB0byB0aGUgc2FtZSBkYXRhIG9iamVjdCBuYW1lLgoKYGBge3J9CnBtICU8PiUKICBkcGx5cjo6bXV0YXRlX2F0KHZhcnMoaWQsIGZpcHMsIHpjdGEpLCBhcy5mYWN0b3IpIAoKZ2xpbXBzZShwbSkKYGBgCgpHcmVhdCEgTm93IHdlIGNhbiBzZWUgdGhhdCB0aGVzZSB2YXJpYWJsZXMgYXJlIG5vdyBmYWN0b3JzIGFzIGluZGljYXRlZCBieSBgPGZjdD5gIGFmdGVyIHRoZSB2YXJpYWJsZSBuYW1lLgoKIyMjIFBhY2thZ2VzIHRvIGdldCBhIHNlbnNlIG9mIHRoZSBkYXRhCgpUaGUgYHNraW0oKWAgZnVuY3Rpb24gb2YgdGhlIGBza2ltcmAgcGFja2FnZSBpcyBhbHNvIHJlYWxseSBoZWxwZnVsIGZvciBnZXR0aW5nIGEgZ2VuZXJhbCBzZW5zZSBvZiB5b3VyIGRhdGEuCgpgYGB7cn0Kc2tpbShwbSkKYGBgCgpOb3RpY2UgaG93IHRoZXJlIGlzIGEgY29sdW1uIGNhbGxlZCBgbl9taXNzaW5nYCBhYm91dCB0aGUgbnVtYmVyIG9mIHZhbHVlcyB0aGF0IGFyZSBtaXNzaW5nLiBJdCBsb29rcyBsaWtlIG91ciBkYXRhIGlzIHZlcnkgY29tcGxldGUgYW5kIHdlIGRvIG5vdCBoYXZlIGFueSBtaXNzaW5nIGRhdGEuIFRoaXMgaXMgYWxzbyBpbmRpY2F0ZWQgYnkgdGhlIGBjb21wbGV0ZV9yYXRlYCB2YXJpYWJsZSwgd2hpY2ggc2hvd3MgdGhlIHJhdGlvIG9mIGNvbXBsZXRlbmVzcywgaW4gb3VyIGNhc2UgYWxsIHZhcmlhYmxlcyBoYXZlIGEgdmFsdWUgb2YgMSBpbmRpY2F0aW5nIHRoZXkgYXJlIGZ1bGx5IGNvbXBsZXRlLgoKVGhlIGBuX3VucWl1ZWAgY29sdW1uIHNob3dzIHVzIHRoZSBudW1iZXIgb2YgdW5pcXVlIHZhbHVlcyBmb3IgZWFjaCBvZiBvdXIgY29sdW1ucy4gV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSA0OSBzdGF0ZXMgcmVwcmVzZW50ZWQgaW4gdGhlIGRhdGEsIGFuZCB3ZSBrbm93IHRoYXQgdGhlIGRhdGEgc2hvdWxkIGJlIG9mIHRoZSBjb250aWd1b3VzIHN0YXRlcy4gTGV0J3MgdGFrZSBhIGxvb2sgdG8gc2VlIHdoaWNoIHN0YXRlcyBhcmUgaW5jbHVkZWQ6CgojIyMjIHsuc2Nyb2xsYWJsZSB9CmBgYHtyfQojIFNjcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhCnBtICU+JSAKICBkaXN0aW5jdChzdGF0ZSkgJT4lCiAgcHJpbnQobiA9IDFlMykKYGBgCiMjIyMKCkxvb2tzIGxpa2UgIkRpc3RyaWN0IG9mIENvbHVtYmlhIiBpcyBiZWluZyBpbmNsdWRlZCBhcyBhIHN0YXRlLiBXZSBjYW4gc2VlIHRoYXQgaW5kZWVkIEFsYXNrYSBhbmQgSGF3YWlpIGFyZSBub3QgaW5jbHVkZWQgaW4gdGhlIGRhdGEuCgpIZXJlIGlzIGFub3RoZXIgbWV0aG9kIG9mIGxvb2tpbmcgYXQgdGhlIGRhdGEgdXNpbmcgdGhlIGBkZlN1bW1hcnkoKWAgZnVuY3Rpb24gb2YgdGhlIGBzdW1tYXJ5dG9vbHNgcGFja2FnZS4gV2UgbmVlZCB0byBjb3B5IGFuZCBwYXN0ZSB0aGUgb3V0cHV0IGludG8gdGhlIHJtYXJrZG93bi4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmRmU3VtbWFyeShwbSwgcGxhaW4uYXNjaWkgPSBGQUxTRSwgc3R5bGUgPSAiZ3JpZCIsIAogICAgICAgICAgZ3JhcGgubWFnbmlmID0gMC40NSwgIHRtcC5pbWcuZGlyID0gInRtcCIpCmBgYAoKKipEaW1lbnNpb25zOioqIDg3NiB4IDUwICAKKipEdXBsaWNhdGVzOioqIDAgIAoKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCBObyB8IFZhcmlhYmxlICAgICAgICAgICAgICAgICAgICAgfCBTdGF0cyAvIFZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRnJlcXMgKCUgb2YgVmFsaWQpICB8IEdyYXBoICAgICAgICAgICAgICAgfCBWYWxpZCAgfCBNaXNzaW5nIHwKKz09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSs9PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09Kz09PT09PT09PSsKfCAxICB8IGlkXCAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gMTAwMy4wMDFcICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICFbXSh0bXAvZHMwMTAxLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtmYWN0b3JdICAgICAgICAgICAgICAgICAgICAgfCAyXC4gMTAyNy4wMDAxXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gMTAzMy4xMDAyXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gMTA0OS4xMDAzXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gMTA1NS4wMDFcICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gMTA2OS4wMDAzXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gMTA3My4wMDIzXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gMTA3My4xMDA1XCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gMTA3My4xMDA5XCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIDEwNzMuMTAxXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDg2NiBvdGhlcnMgXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgODY2ICg5OC45JSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyICB8IHZhbHVlXCAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxMC44ICgyLjYpXCAgICAgICAgICAgICAgICAgIHwgODc1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTAyLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzIDwgMTEuMiA8IDIzLjJcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMuMSAoMC4yKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzICB8IGZpcHNcICAgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gMTAwM1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICFbXSh0bXAvZHMwMTAzLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtmYWN0b3JdICAgICAgICAgICAgICAgICAgICAgfCAyXC4gMTAyN1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gMTAzM1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gMTA0OVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gMTA1NVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gMTA2OVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gMTA3M1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgOCAoIDAuOSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gMTA4OVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gMTA5N1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMiAoIDAuMiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIDExMDFcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDU1OSBvdGhlcnMgXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgODU4ICg5OC4wJSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0ICB8IGxhdFwgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAzOC41ICg0LjYpXCAgICAgICAgICAgICAgICAgIHwgODc2IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTA0LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAyNS41IDwgMzkuMyA8IDQ4LjRcICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDYuNiAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA1ICB8IGxvblwgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAtOTEuNyAoMTUpXCAgICAgICAgICAgICAgICAgIHwgODc2IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTA1LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtMTI0LjIgPCAtODcuNSA8IC02OFwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE4LjUgKC0wLjIpICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA2ICB8IHN0YXRlXCAgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gQ2FsaWZvcm5pYVwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgODUgKCA5LjclKVwgICAgICAgICB8ICFbXSh0bXAvZHMwMTA2LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtjaGFyYWN0ZXJdICAgICAgICAgICAgICAgICAgfCAyXC4gT2hpb1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNDQgKCA1LjAlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gSWxsaW5vaXNcICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMzggKCA0LjMlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gSW5kaWFuYVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMzYgKCA0LjElKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gTm9ydGggQ2Fyb2xpbmFcICAgICAgICAgICAgICAgICAgICAgIHwgMzUgKCA0LjAlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gUGVubnN5bHZhbmlhXCAgICAgICAgICAgICAgICAgICAgICAgIHwgMzIgKCAzLjclKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gTWljaGlnYW5cICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMzAgKCAzLjQlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gRmxvcmlkYVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjkgKCAzLjMlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gR2VvcmdpYVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjggKCAzLjIlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIFRleGFzXCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMjcgKCAzLjElKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDM5IG90aGVycyBdICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNDkyICg1Ni4yJSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA3ICB8IGNvdW50eVwgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gSmVmZmVyc29uXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTggKCAyLjElKVwgICAgICAgICB8ICFbXSh0bXAvZHMwMTA3LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtjaGFyYWN0ZXJdICAgICAgICAgICAgICAgICAgfCAyXC4gQ29va1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTIgKCAxLjQlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gSGFtaWx0b25cICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTEgKCAxLjMlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gTGFrZVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTEgKCAxLjMlKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gTG9zIEFuZ2VsZXNcICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTAgKCAxLjElKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gV2F5bmVcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTAgKCAxLjElKVwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gV2FzaGluZ3RvblwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgOSAoIDEuMCUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gQ3V5YWhvZ2FcICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNyAoIDAuOCUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gSmFja3NvblwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNyAoIDAuOCUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIE1hZGlzb25cICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNyAoIDAuOCUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDQ2MSBvdGhlcnMgXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNzc0ICg4OC40JSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA4ICB8IGNpdHlcICAgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gTm90IGluIGEgY2l0eVwgICAgICAgICAgICAgICAgICAgICAgIHwgMTAzICgxMS44JSlcICAgICAgICB8ICFbXSh0bXAvZHMwMTA4LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtjaGFyYWN0ZXJdICAgICAgICAgICAgICAgICAgfCAyXC4gTmV3IFlvcmtcICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgOSAoIDEuMCUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gQ2xldmVsYW5kXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNiAoIDAuNyUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gQmFsdGltb3JlXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gQ2hpY2Fnb1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gRGV0cm9pdFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gTWlsd2F1a2VlXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gTmV3IEhhdmVuXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gUGhpbGFkZWxwaGlhXCAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIFNwcmluZ2ZpZWxkXCAgICAgICAgICAgICAgICAgICAgICAgIHwgNSAoIDAuNiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDU5NyBvdGhlcnMgXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNzIzICg4Mi41JSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA5ICB8IENNQVFcICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA4LjQgKDMpXCAgICAgICAgICAgICAgICAgICAgIHwgNjAxIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTA5LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxLjYgPCA4LjYgPCAyMy4xXCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMuNyAoMC40KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxMCB8IHpjdGFcICAgICAgICAgICAgICAgICAgICAgICAgfCAxXC4gMTAyMlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICFbXSh0bXAvZHMwMTEwLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtmYWN0b3JdICAgICAgICAgICAgICAgICAgICAgfCAyXC4gMTEwM1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMiAoIDAuMiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzXC4gMTIwMVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA0XC4gMTYwOFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMiAoIDAuMiUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1XC4gMTgzMlwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2XC4gMTg0MFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3XC4gMTg2M1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4XC4gMTkwNFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5XC4gMjExM1wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMFwuIDIxMTlcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMSAoIDAuMSUpXCAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBbIDgzMiBvdGhlcnMgXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgODY0ICg5OC42JSkgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxMSB8IHpjdGFfYXJlYVwgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxODMxNzM0ODEuOSAoNTQyNTk4ODc4LjUpXCAgIHwgODQyIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTExLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxNTQ1OSA8IDM3NjUzNTYwLjUgPCA4MTY0ODIwNjI1XCAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE0NTgzNjkwNi41ICgzKSAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxMiB8IHpjdGFfcG9wXCAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAyNDIyNy42ICgxNzc3Mi4yKVwgICAgICAgICAgIHwgODM3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTEyLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMjIwMTQgPCA5NTM5N1wgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDI1MjA3LjggKDAuNykgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxMyB8IGltcF9hNTAwXCAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAyNC43ICgxOS4zKVwgICAgICAgICAgICAgICAgIHwgODE2IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTEzLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMjUuMSA8IDY5LjZcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDM2LjUgKDAuOCkgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxNCB8IGltcF9hMTAwMFwgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAyNC4zICgxOClcICAgICAgICAgICAgICAgICAgIHwgODYwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE0LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMjQuNSA8IDY3LjVcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMzLjMgKDAuNykgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxNSB8IGltcF9hNTAwMFwgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxOS45ICgxNC43KVwgICAgICAgICAgICAgICAgIHwgODcwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE1LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjEgPCAxOS4xIDwgNzQuNlwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIzLjMgKDAuNykgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxNiB8IGltcF9hMTAwMDBcICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxNS44ICgxMy44KVwgICAgICAgICAgICAgICAgIHwgODcwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE2LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjEgPCAxMi40IDwgNzIuMVwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE5LjYgKDAuOSkgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxNyB8IGltcF9hMTUwMDBcICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxMy40ICgxMy4xKVwgICAgICAgICAgICAgICAgIHwgODcwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE3LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjEgPCA5LjcgPCA3MS4xXCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE3LjMgKDEpICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxOCB8IGNvdW50eV9hcmVhXCAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAzNzY4NzAxOTkyLjEgKDYyMTI4Mjk1NTMuNilcIHwgNTY0IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE4LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzMzcwMzUxMiA8IDE2OTA4MjY1NjYuNSA8IDUxOTQ3MjI5NTA5XCAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE3NjE2NTU5MTEuNSAoMS42KSAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAxOSB8IGNvdW50eV9wb3BcICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA2ODcyOTguNCAoMTI5MzQ4OC43KVwgICAgICAgIHwgNTY0IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTE5LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3ODMgPCAyODA3MzAuNSA8IDk4MTg2MDVcICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDY0MjIxMSAoMS45KSAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyMCB8IGxvZ19kaXN0X3RvX3ByaXNlY1wgICAgICAgICAgfCBNZWFuIChzZCkgOiA2LjIgKDEuNClcICAgICAgICAgICAgICAgICAgIHwgODcwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTIwLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtMS41IDwgNi40IDwgMTAuNVwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEuNyAoMC4yKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyMSB8IGxvZ19wcmlfbGVuZ3RoXzUwMDBcICAgICAgICAgfCBNZWFuIChzZCkgOiA5LjggKDEuMSlcICAgICAgICAgICAgICAgICAgIHwgNTg2IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTIxLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4LjUgPCAxMC4xIDwgMTJcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIuMiAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyMiB8IGxvZ19wcmlfbGVuZ3RoXzEwMDAwXCAgICAgICAgfCBNZWFuIChzZCkgOiAxMC45ICgxLjEpXCAgICAgICAgICAgICAgICAgIHwgNjg3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTIyLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5LjIgPCAxMS4yIDwgMTNcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIgKDAuMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyMyB8IGxvZ19wcmlfbGVuZ3RoXzE1MDAwXCAgICAgICAgfCBNZWFuIChzZCkgOiAxMS41ICgxLjEpXCAgICAgICAgICAgICAgICAgIHwgNzI2IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTIzLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5LjYgPCAxMS43IDwgMTMuNlwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEuNSAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyNCB8IGxvZ19wcmlfbGVuZ3RoXzI1MDAwXCAgICAgICAgfCBNZWFuIChzZCkgOiAxMi4yICgxLjEpXCAgICAgICAgICAgICAgICAgIHwgNzg3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI0LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMC4xIDwgMTIuNSA8IDE0LjRcICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEuNCAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyNSB8IGxvZ19wcmlzZWNfbGVuZ3RoXzUwMFwgICAgICAgfCBNZWFuIChzZCkgOiA3ICgxKVwgICAgICAgICAgICAgICAgICAgICAgIHwgMzgyIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI1LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA2LjIgPCA2LjIgPCA5LjRcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEuNiAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyNiB8IGxvZ19wcmlzZWNfbGVuZ3RoXzEwMDBcICAgICAgfCBNZWFuIChzZCkgOiA4LjYgKDAuOClcICAgICAgICAgICAgICAgICAgIHwgNTkxIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI2LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA3LjYgPCA4LjcgPCAxMC41XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEuNiAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyNyB8IGxvZ19wcmlzZWNfbGVuZ3RoXzUwMDBcICAgICAgfCBNZWFuIChzZCkgOiAxMS4zICgwLjgpXCAgICAgICAgICAgICAgICAgIHwgODUyIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI3LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA4LjUgPCAxMS40IDwgMTIuOFwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDAuOSAoMC4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyOCB8IGxvZ19wcmlzZWNfbGVuZ3RoXzEwMDAwXCAgICAgfCBNZWFuIChzZCkgOiAxMi40ICgwLjcpXCAgICAgICAgICAgICAgICAgIHwgODY3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI4LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5LjIgPCAxMi41IDwgMTMuOFwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEgKDAuMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAyOSB8IGxvZ19wcmlzZWNfbGVuZ3RoXzE1MDAwXCAgICAgfCBNZWFuIChzZCkgOiAxMyAoMC43KVwgICAgICAgICAgICAgICAgICAgIHwgODY5IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTI5LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA5LjYgPCAxMy4xIDwgMTQuNFwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEgKDAuMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzMCB8IGxvZ19wcmlzZWNfbGVuZ3RoXzI1MDAwXCAgICAgfCBNZWFuIChzZCkgOiAxMy44ICgwLjcpXCAgICAgICAgICAgICAgICAgIHwgODcwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTMwLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxMC4xIDwgMTMuOSA8IDE1LjJcICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEgKDAuMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzMSB8IGxvZ19uZWlfMjAwOF9wbTI1X3N1bV8xMDAwMFwgfCBNZWFuIChzZCkgOiA0ICgyLjQpXCAgICAgICAgICAgICAgICAgICAgIHwgODI4IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTMxLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNC4zIDwgOS4xXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMuNSAoMC42KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzMiB8IGxvZ19uZWlfMjAwOF9wbTI1X3N1bV8xNTAwMFwgfCBNZWFuIChzZCkgOiA0LjcgKDIuMilcICAgICAgICAgICAgICAgICAgIHwgODU1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTMyLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNSA8IDkuNFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIuOSAoMC41KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzMyB8IGxvZ19uZWlfMjAwOF9wbTI1X3N1bV8yNTAwMFwgfCBNZWFuIChzZCkgOiA1LjcgKDIuMSlcICAgICAgICAgICAgICAgICAgIHwgODYwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTMzLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNS45IDwgOS43XCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIuNiAoMC40KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzNCB8IGxvZ19uZWlfMjAwOF9wbTEwX3N1bV8xMDAwMFwgfCBNZWFuIChzZCkgOiA0LjMgKDIuMylcICAgICAgICAgICAgICAgICAgIHwgODI5IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM0LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNC42IDwgOS4zXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMuNCAoMC41KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzNSB8IGxvZ19uZWlfMjAwOF9wbTEwX3N1bV8xNTAwMFwgfCBNZWFuIChzZCkgOiA1LjEgKDIuMilcICAgICAgICAgICAgICAgICAgIHwgODU1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM1LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNS40IDwgOS43XCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIuOCAoMC40KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzNiB8IGxvZ19uZWlfMjAwOF9wbTEwX3N1bV8yNTAwMFwgfCBNZWFuIChzZCkgOiA2LjEgKDIpXCAgICAgICAgICAgICAgICAgICAgIHwgODYwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM2LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNi40IDwgOS45XCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIuNCAoMC4zKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzNyB8IHBvcGRlbnNfY291bnR5XCAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA1NTEuOCAoMTcxMS41KVwgICAgICAgICAgICAgIHwgNTY0IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM3LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwLjMgPCAxNTYuNyA8IDI2ODIxLjlcICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDQ3MCAoMy4xKSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzOCB8IHBvcGRlbnNfemN0YVwgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxMjc5LjcgKDI3NTcuNSlcICAgICAgICAgICAgIHwgODQwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM4LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNjEwLjMgPCAzMDQxOC44XCAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEyODEuNCAoMi4yKSAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCAzOSB8IG5vaHNcICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA3ICg3LjIpXCAgICAgICAgICAgICAgICAgICAgIHwgMjE1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTM5LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNS4xIDwgMTAwXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDYuMSAoMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0MCB8IHNvbWVoc1wgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxMC4yICg2LjIpXCAgICAgICAgICAgICAgICAgIHwgMjMwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQwLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgOS40IDwgNzIuMlwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDggKDAuNikgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0MSB8IGhzXCAgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAzMC4zICgxMS40KVwgICAgICAgICAgICAgICAgIHwgMzQ3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQxLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMzAuOCA8IDEwMFwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEyLjMgKDAuNCkgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0MiB8IHNvbWVjb2xsZWdlXCAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAyMS42ICg4LjYpXCAgICAgICAgICAgICAgICAgIHwgMjQwIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQyLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMjEuMyA8IDEwMFwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDcuMiAoMC40KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0MyB8IGFzc29jaWF0ZVwgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA3LjEgKDQpXCAgICAgICAgICAgICAgICAgICAgIHwgMTU3IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQzLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNy4xIDwgNzEuNFwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDMuOSAoMC42KSAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0NCB8IGJhY2hlbG9yXCAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxNC45ICg5LjcpXCAgICAgICAgICAgICAgICAgIHwgMzAxIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQ0LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMTIuOSA8IDEwMFwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDEwLjQgKDAuNykgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0NSB8IGdyYWRcICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA4LjkgKDguNilcICAgICAgICAgICAgICAgICAgIHwgMjQ1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQ1LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNi43IDwgMTAwXCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDcuMSAoMSkgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0NiB8IHBvdlwgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAxNSAoMTEuMylcICAgICAgICAgICAgICAgICAgIHwgMzQ1IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQ2LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgMTIuMSA8IDY1LjlcICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE0LjcgKDAuOCkgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0NyB8IGhzX29ybGVzc1wgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA0Ny41ICgxNi44KVwgICAgICAgICAgICAgICAgIHwgNDY0IGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTQ3LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAwIDwgNDguNyA8IDEwMFwgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIxLjIgKDAuNCkgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0OCB8IHVyYzIwMTNcICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAyLjkgKDEuNSlcICAgICAgICAgICAgICAgICAgIHwgMSA6IDIwMyAoMjMuMiUpXCAgICB8ICFbXSh0bXAvZHMwMTQ4LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgMiA6IDE2MyAoMTguNiUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxIDwgMyA8IDZcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMyA6IDIyOCAoMjYuMCUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIgKDAuNSkgICAgICAgICAgICAgICAgICAgICAgIHwgNCA6IDEyMyAoMTQuMCUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSA6IDEwMSAoMTEuNSUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNiA6ICA1OCAoIDYuNiUpICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA0OSB8IHVyYzIwMDZcICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiAzICgxLjUpXCAgICAgICAgICAgICAgICAgICAgIHwgMSA6IDE5NSAoMjIuMyUpXCAgICB8ICFbXSh0bXAvZHMwMTQ5LnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgMiA6IDE2MiAoMTguNSUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxIDwgMyA8IDZcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMyA6IDIyMSAoMjUuMiUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDIgKDAuNSkgICAgICAgICAgICAgICAgICAgICAgIHwgNCA6IDEyNyAoMTQuNSUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNSA6IDExNSAoMTMuMSUpXCAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgNiA6ICA1NiAoIDYuNCUpICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKfCA1MCB8IGFvZFwgICAgICAgICAgICAgICAgICAgICAgICAgfCBNZWFuIChzZCkgOiA0My43ICgxOS42KVwgICAgICAgICAgICAgICAgIHwgNTgxIGRpc3RpbmN0IHZhbHVlcyB8ICFbXSh0bXAvZHMwMTUwLnBuZykgfCA4NzZcICAgfCAwXCAgICAgIHwKfCAgICB8IFtudW1lcmljXSAgICAgICAgICAgICAgICAgICAgfCBtaW4gPCBtZWQgPCBtYXg6XCAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAoMTAwJSkgfCAoMCUpICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCA1IDwgNDAuMiA8IDE0M1wgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKfCAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJUVIgKENWKSA6IDE4ICgwLjQpICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgfCAgICAgICAgIHwKKy0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tKy0tLS0tLS0tLSsKCgpXZSBjYW4gc2VlIHRoYXQgZm9yIG1hbnkgdmFyaWFibGVzIHRoZXJlIGFyZSBtYW55IGxvdyB2YWx1ZXMgYXMgdGhlIGRpc3RyaWJ1dGlvbiBzaG93cyB0d28gcGVha3MsIG9uZSBuZWFyIHplcm8gYW5kIGFub3RoZXIgd2l0aCBhIGhpZ2hlciB2YWx1ZS4gVGhpcyBpcyB0cnVlIGZvciB0aGUgaW1wIHZhcmlhYmxlcyAobWVhc3VyZXMgb2YgZGV2ZWxvcG1lbnQpLCB0aGUgbmVpIHZhcmlhYmxlcyAobWVhc3VyZXMgb2YgZW1pc3Npb24gc291cmNlcykgYW5kIHRoZSByb2FkIGRlbnNpdHkgdmFyaWFibGVzLiBXZSBjYW4gYWxzbyBzZWUgdGhhdCB0aGUgcmFuZ2Ugb2Ygc29tZSBvZiB0aGUgdmFyaWFibGVzIGlzIHZlcnkgbGFyZ2UsIGluIHBhcnRpY3VsYXIgdGhlIGFyZWEgYW5kIHBvcHVsYXRpb24gcmVsYXRlZCB2YXJpYWJsZXMuCgojIyMgRXZhbHVhdGUgY29ycmVsYXRpb24gYW1vbmcgcG9zc2libGUgcHJlZGljdG9ycwpJbiBwcmVkaWN0aW9uIGFuYWx5c2VzLCBpdCBpcyBhbHNvIHVzZWZ1bCB0byBldmFsdWF0ZSBpZiBhbnkgb2YgdGhlIHZhcmlhYmxlcyBhcmUgY29ycmVsYXRlZC4KCkludHVpdGl2ZWx5IHdlIGNhbiBleHBlY3Qgc29tZSBvZiBvdXIgdmFyaWFibGVzIHRvIGJlIGNvcnJlbGF0ZWQuCgpMZXQncyBmaXJzdCB0YWtlIGEgbG9vayBhdCBhbGwgb2Ygb3VyIG51bWVyaWMgdmFyaWFiZWxzIHdpdGggdGhlYGNvcnJwbG90YCBwYWNrYWdlOgpUaGUgYGNvcnJwbG90YCBwYWNrYWdlIGlzIGFub3RoZXIgb3B0aW9uIHRvIGxvb2sgYXQgY29ycmVsYXRpb24gYW1vbmcgcG9zc2libGUgcHJlZGljdG9ycy4gVGhpcyBpcyBhIGdyZWF0IG9wdGlvbiBpZiB3ZSBoYXZlIG1hbnkgcHJlZGljdG9ycy4gCkZpcnN0IHdlIG5lZWQgdG8gY3JlYXRlIGEgY29ycmVsYXRpb24gbWF0cml4IHVzaW5nIHRoZSBgY29yKClgIGZ1bmN0aW9uIG9mIHRoZSBgc3RhdHNgIHBhY2thZ2UgKHdoaWNoIGlzIGxvYWRlZCBhdXRvbWF0aWNhbGx5KS4KCmBgYHtyfQojbGlicmFyeShSQ29sb3JCcmV3ZXIpICNuZWVkIHRoaXMgcGFja2FnZSBmb3IgdGhlIGNvbG9yClBNX2NvciA8LSBjb3IocG0gJT4lIGRwbHlyOjpzZWxlY3RfaWYoaXMubnVtZXJpYykpCmNvcnJwbG90Ojpjb3JycGxvdChQTV9jb3IsIHRsLmNleCA9IDAuNSkKCmNvcnJwbG90KGFicyhQTV9jb3IpLCBvcmRlciA9ICJBT0UiLCB0bC5jZXggPSAwLjUsICBjbC5saW0gPSBjKDAsIDEpKQoKCmNvcnJwbG90KFBNX2NvciwgZGlhZyA9IEZBTFNFLCBvcmRlciA9ICJGUEMiLAogICAgICAgICB0bC5wb3MgPSAidGQiLCB0bC5jZXggPSAwLjUsIG1ldGhvZCA9ICJjb2xvciIsIHR5cGUgPSAidXBwZXIiLCBjb2wgPSBicmV3ZXIucGFsKG4gPSA4LCBuYW1lID0gIlB1T3IiKSkKCmNvcnJwbG90KFBNX2NvciwgZGlhZyA9IEZBTFNFLAogICAgICAgICB0bC5wb3MgPSAidGQiLCB0bC5jZXggPSAwLjUsIG1ldGhvZCA9ICJjb2xvciIsIHR5cGUgPSAidXBwZXIiKQoKYGBgCgogVXNpbmcgYGdnY29ycGxvdGAKYGBge3J9CmxpYnJhcnkoZ2djb3JycGxvdCkKZ2djb3JycGxvdChQTV9jb3IsIGhjLm9yZGVyID0gVFJVRSwgdHlwZSA9ICJsb3dlciIsIHRsLmNleCA9IDUpCgpgYGAKCldlIGNhbiBzZWUgdGhhdCB0aGUgdGhlIGRldmVsb3BtZW50IHZhcmlhYmxlcyAoaW1wKSB2YXJpYWJsZXMgYXJlIGNvcnJlbGF0ZWQgd2l0aCBlYWNoIG90aGVyIGFzIHdlIG1pZ2h0IGV4cGVjdC4gV2UgYWxzbyBzZWUgdGhhdCB0aGUgcm9hZCBkZW5zaXR5IHZhcmlhYmxlcyBzZWVtIHRvIGJlIGNvcnJlbGF0ZWQgd2l0aCBlYWNoIG90aGVyLCBhbmQgdGhlIGVtaXNzaW9uIHZhcmlhYmxlcyBzZWVtIHRvIGJlIGNvcnJlbGF0ZWQgd2l0aCBlYWNoIG90aGVyLiBXZSBjYW4gdGFrZSBhIGNsb3NlciBsb29rICB1c2luZyB0aGUgYGdnY29ycigpYCBmdW5jdGlvbiBhbmQgdGhlIGBnZ3BhaXJzKClgIGZ1bmN0aW9uIG9mIHRoZSBgR0dhbGx5YCBwYWNrYWdlLiBUbyBzZWxlY3Qgb3VyIHZhcmlhYmxlcyBvZiBpbnRlcmVzdCB3ZSBjYW4gdXNlIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIHdpdGggdGhlIGBjb250YWlucygpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlLiAKCkZpcnN0IGxldCdzIGxvb2sgYXQgdGhlIGltcC9kZXZlbG9wbWVudCB2YXJpYWJsZXMuIApgYGB7ciwgb3V0LndpZHRoID0gIjQwMHB4In0Kc2VsZWN0KHBtLCBjb250YWlucygiaW1wIikpICU+JQogIGdnY29ycihwYWxldHRlID0gIlJkQnUiLCBsYWJlbCA9IFRSVUUpCgpzZWxlY3QocG0sIGNvbnRhaW5zKCJpbXAiKSkgJT4lCiAgZ2dwYWlycygpCiAgCmBgYAoKSW5kZWVkLCB3ZSBjYW4gc2VlIHRoYXQgaW1wX2ExMDAwIGFuZCBpbXBfYTUwMCBhcmUgcGVyZmVjdGx5IGNvcnJlbGF0ZWQsIGFzIHdlbGwgYXMgaW1wX2ExMDAwMCwgaW1wX2ExNTAwMC4KCgpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHJvYWQgZGVuc2l0eSBkYXRhOgoKYGBge3IsIGZpZy53ZWlnaHQ9MTJ9CnNlbGVjdChwbSwgY29udGFpbnMoInByaSIpKSAlPiUKICBnZ2NvcnIocGFsZXR0ZSA9ICJSZEJ1IiwgIGhqdXN0ID0gLjg1LCBzaXplID0gMywKICAgICAgIGxheW91dC5leHA9MiwgbGFiZWwgPSBUUlVFKQoKc2VsZWN0KHBtLCBjb250YWlucygicHJpIikpICU+JQogIGdncGFpcnMoKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB3aGlsZSBzb21lIG9mIHRoZSByb2FkIGRlbnNpdHkgdmFyaWFibGVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIG9uZSBhbm90aGVyIChvciBhbnRpLWNvcnJlbGF0ZWQpLCB0aGVyZSBhcnIgc29tZSB2YXJpYWJsZXMgdGhhdCBhcmUgbGVzcyBjb3JyZWxhdGVkIHdpdGggb25lIGFub3RoZXIuCgpGaW5hbGx5IGxldCdzIGxvb2sgYXQgdGhlIGVtaXNzaW9uIHZhcmlhYmxlcy4KCmBgYHtyfQpzZWxlY3QocG0sIGNvbnRhaW5zKCJuZWkiKSkgJT4lCiAgZ2djb3JyKHBhbGV0dGUgPSAiUmRCdSIsICBoanVzdCA9IC44NSwgc2l6ZSA9IDMsCiAgICAgICBsYXlvdXQuZXhwPTIsIGxhYmVsID0gVFJVRSkKCnNlbGVjdChwbSwgY29udGFpbnMoIm5laSIpKSAlPiUKICBnZ3BhaXJzKCkKYGBgCgpXZSB3b3VsZCBhbHNvIGV4cGVjdCB0aGUgcG9wdWxhdGlvbiBkZW5zaXR5IGRhdGEgbWlnaHQgY29ycmVsYXRlIHdpdGggc29tZSBvZiB0aGVzZSB2YXJpYWJsZXMuIExldCdzIHRha2UgYSBsb29rLgoKYGBge3J9CnBtICU+JQpzZWxlY3QobG9nX25laV8yMDA4X3BtMjVfc3VtXzEwMDAwLCBwb3BkZW5zX2NvdW50eSwgbG9nX3ByaV9sZW5ndGhfMTAwMDAsIGltcF9hMTAwMDApICU+JQogIGdnY29ycihwYWxldHRlID0gIlJkQnUiLCAgaGp1c3QgPSAuODUsIHNpemUgPSAzLAogICAgICAgbGF5b3V0LmV4cD0yLCBsYWJlbCA9IFRSVUUpCgpwbSAlPiUKc2VsZWN0KGxvZ19uZWlfMjAwOF9wbTI1X3N1bV8xMDAwMCwgcG9wZGVuc19jb3VudHksIGxvZ19wcmlfbGVuZ3RoXzEwMDAwLCBpbXBfYTEwMDAwLCBjb3VudHlfcG9wKSAlPiUKICBnZ3BhaXJzKCkKYGBgCgoKSW50ZXJlc3RpbmcsIHNvIHRoZXNlIHZhcmlhYmxlcyBkb24ndCBhcHBlYXIgdG8gYmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHRoZXJlZm9yZSB3ZSBtaWdodCBuZWVkIHZhcmlhYmxlcyBmcm9tIGVhY2ggb2YgdGhlIGNhdGVnb3JpZXMgdG8gcHJlZGljdCBvdXIgbW9uaXRvciBQTX4yLjV+IHBvbGx1dGlvbiB2YWx1ZXMuCgpXZSBzZWVtIHRvIGhhdmUgc29tZSBwcmV0dHkgZXh0cmVtZSBwb3B1bGF0aW9uIHZhbHVlcyB0aG91Z2gsIHNvIGxldCdzIHNlZSB3aGF0IGhhcHBlbnMgd2hlbiB3ZSB0YWtlIHRoZSBsb2cgdmFsdWUuCgpgYGB7cn0KcG0gJT4lCiAgbXV0YXRlKGxvZ19wb3BkZW5zX2NvdW50eT0gbG9nKHBvcGRlbnNfY291bnR5KSkgJT4lCnNlbGVjdChsb2dfbmVpXzIwMDhfcG0yNV9zdW1fMTAwMDAsIGxvZ19wb3BkZW5zX2NvdW50eSwgbG9nX3ByaV9sZW5ndGhfMTAwMDAsIGltcF9hMTAwMDApICU+JQogIGdnY29ycihwYWxldHRlID0gIlJkQnUiLCAgaGp1c3QgPSAuODUsIHNpemUgPSAzLAogICAgICAgbGF5b3V0LmV4cD0yLCBsYWJlbCA9IFRSVUUpCgpwbSAlPiUKICBtdXRhdGUobG9nX3BvcGRlbnNfY291bnR5PSBsb2cocG9wZGVuc19jb3VudHkpKSAlPiUKICBtdXRhdGUobG9nX3BvcF9jb3VudHkgPSBsb2coY291bnR5X3BvcCkpICU+JQpzZWxlY3QobG9nX25laV8yMDA4X3BtMjVfc3VtXzEwMDAwLCBsb2dfcG9wZGVuc19jb3VudHksIGxvZ19wcmlfbGVuZ3RoXzEwMDAwLCBpbXBfYTEwMDAwLCBsb2dfcG9wX2NvdW50eSkgJT4lCiAgZ2dwYWlycygpCmBgYAoKSW5kZWVkIHRoaXMgaW5jcmVhc2VkIHRoZSBjb3JyZWxhdGlvbiwgYnV0IHZhcmlhYmxlcyBmcm9tIGVhY2ggb2YgdGhlc2UgY2F0ZWdvcmllcyBtYXkgc3RpbGwgcHJvdmUgdG8gYmUgdXNlZnVsIGZvciBwcmVkaWN0aW9uLgoKCgoKIyMgRGF0YSBBbmFseXNpcwoKTm93IHRoYXQgd2UgaGF2ZSBhIHNlbnNlIG9mIHdoYXQgb3VyIGRhdGEgaXMgbGlrZSB3ZSBjYW4gZ2V0IHN0YXJ0ZWQgd2l0aCBkYXRhIGFuYWx5c2lzLgoKIyMjIFRoZSBtYWNoaW5lIGxlYXJuaW5nIHByb2Nlc3MKClRoZXJlIGFyZSB0d28gbWFqb3IgdHlwZXMgb2YgbWFjaGluZSBsZWFybmluZzogIAoKMSkgVW5zdXBlcnZpc2VkICAKMikgU3VwZXJ2aXNlZCAgCgpVbnN1cGVydmlzZWQgbGVhcm5pbmcgaXMgdXNlZCB0byBsZWFybiBhYm91dCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHdpdGhvdXQga25vd2luZyBtdWNoIGFib3V0IHRoZSBkYXRhLiBXZSBsZXQgdGhlIGRhdGEgcmV2ZWFsIHByb3BlcnRpZXMgYWJvdXQgaXRzZWxmLiBFeGFtcGxlcyBpbmNsdWRlIGNsdXN0ZXJpbmcgdGhlIGRhdGEgaW50byBncm91cHMgb3IgcmVkdWNpbmcgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBkYXRhIHVzaW5nIG1ldGhvZHMgbGlrZSBbcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpc10oaHR0cHM6Ly9tZWRpdW0uY29tL0BzYXZhc3RhbWlya28vcGNhLWEtbGluZWFyLXRyYW5zZm9ybWF0aW9uLWY4YWFjZDRlYjAwNyl7dGFyZ2V0PSJfYmxhbmsifSAod2hpY2ggd2Ugd2lsbCBkZXNjcmliZSBpbiBtb3JlIGRldGFpbCBsYXRlcikgdG8gY2FwdHVyZSBwYXR0ZXJucyBvZiB2YXJhaW5jZSB3aXRoaW4gdGhlIGRhdGEuCgpgYGB7ciwgb3V0LndpZHRoID0gIjQlIiwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIifQppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL21pcm8ubWVkaXVtLmNvbS9tYXgvMTQwMC8xKmxoa0NPb2RDTVowLVNTemlFRHB3cEEucG5nIikKYGBgCgojIyMjIFtbc291cmNlXShodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vc3VwZXJ2aXNlZC12cy11bnN1cGVydmlzZWQtbGVhcm5pbmctMTRmNjhlMzJlYThkKV0KCkluIGNvbnRyYXN0LCBpbiBzdXBlcnZpc2VkIGxlYXJuaW5nIHdlIGhhdmUgc29tZSBrbm93bGVnZSBhYm91dCB0aGUgZGF0YSB0aGF0IHdlIHdhbnQgdG8gdXNlIHRvIGNyZWF0ZSBhIG1vZGVsIHRvIGJlIGFibGUgdG8gZ2VuZXJhbGl6ZSBhYm91dCBvdGhlciBzaW1pbGFyIGRhdGEuCgpUaGVyZSBhcmUgdHdvIGRpc3RpbmN0IGdvYWxzIG9mIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZzogIAoKMSkgUHJlZGljdGlvbiAgCjIpIENsYXNzaWZpY2F0aW9uICAKCmBgYHtyLCBvdXQud2lkdGggPSAiNCUiLCBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbiA9ImNlbnRlciJ9CmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vbWlyby5tZWRpdW0uY29tL21heC8xNDAwLzEqQVNZcEZmRGg3WG5yZVUteWdxWG9udy5wbmciKQpgYGAKCiMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS9zdXBlcnZpc2VkLXZzLXVuc3VwZXJ2aXNlZC1sZWFybmluZy0xNGY2OGUzMmVhOGQpXQoKV2Ugd2lsbCBiZSBwZXJmb3JtaW5nIGEgcHJlZGljdGlvbiBhbmFseXNpcyAod2hpY2ggaXMgYWxzbyByZWZlcmVkIHRvIGFzIHJlZ3Jlc3Npb24pLCB3aGljaCBhaW1zIHRvIHByZWRpY3QgKipjb250aW51b3VzIG91dGNvbWUqKiB2YXJpYWJsZXMgZ2l2ZW4gYSBudW1iZXIgb2YgcHJlZGljdG9ycy9leHBsYW5hdG9yeSB2YXJpYWJsZXMvZmVhdHVyZXMvcGFyYW1ldGVycywgYXMgd2UgaGF2ZSBhbHJlYWR5IGRlc2NyaWJlZC4KCkNsYXNzaWZpY2F0aW9uIG9uIHRoZSBvdGhlciBoYW5kIGFpbXMgdG8gZGlzY2VybiBvciBwcmVkaWN0IGdyb3VwIGlkZW50aXR5IGZvciBhICoqY2F0ZWdvcmljYWwgb3V0Y29tZSoqIGJhc2VkIG9uIGEgbnVtYmVyIG9mIHByZWRpY3RvcnMvZXhwbGFuYXRvcnkgdmFyaWFibGVzL2ZlYXR1cmVzL3BhcmFtZXRlcnMuCgpUaGUgb3ZlcmFsbCBwcm9jZXNzIGlzIHRoZSBzYW1lIGluIGVpdGhlciBjYXNlIGFuZCBpbnZvbGVzIHRoZSBmb2xsb3dpbmcgc3RlcHMgKHdoaWNoIHdpbGwgZWFjaCBiZSBleHBsYWluZWQgaW4gZGV0YWlsKTogCgoxKSBEYXRhIGV4cGxvcmF0aW9uICAKCldlIGhhdmUgYWxyZWFkeSBwZXJmb3JtZWQgdGhpcyBzdGVwIHRvIGdldCBhIHNlbnNlIG9mIHRoZSBkYXRhLiBJdCBpcyBpbXBvcnRhbnQgdG8ga25vdyBpZiB3ZSBoYXZlIGBOQWAgdmFsdWVzLCB0byB1bmRlcnN0YW5kIHRoZSBjbGFzcyBvZiB2YXJpYWJsZXMsIGFuZCBpZiB0byBkZXRlcm1pbmUgaWYgdGhlcmUgYXJlIGFueSByZWR1ZGFudCB2YXJpYWJsZXMgdGhhdCBtaWdodCBuZWVkIHRvIHJlbW92ZWQuCgoyKSBEYXRhIHNwbGl0dGluZyAKClRoZSBkYXRhIG5lZWRzIHRvIGJlIHNwbGl0IGludG8gdHdvIHBlaWNlczogYSB0cmFpbmluZyBzZXQgYW5kIGEgdGVzdGluZyBzZXQuIFRoZSB0cmFpbmluZyBzZXQgd2lsbCBiZSB1c2VkIHRvIG9wdGltaXplIHRoZSBtb2RlbCwgd2hpbGUgdGhlIHRlc3Rpbmcgc2V0IHdpbGwgYmUgdXNlZCB0byBldmFsdWF0ZSBtb2RlbCBwZXJmb3JtYW5jZS4gCgozKSBWYXJpYWJsZSBhc3NpZ25tZW50IGFuZCBwcmUtcHJvY2Vzc2luZyAgCgpCb3RoIHRoZSB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhIG5lZWRzIHRvIGJlIHByb2Nlc3NlZCBzbyB0aGF0IHRoZSBkYXRhIGlzIGNvbXBhdGlibGUgYW5kIG9wdGltaXplZCB0byBiZSB1c2VkIHdpdGggdGhlIG1vZGVsLiBUaGlzIGludm9sdmVzIGFzc2lnbmluZyB2YXJpYWJsZXMgdG8gc3BlY2lmaWMgcm9sZXMgd2l0aGluIHRoZSBtb2RlbCBhbmQgcHJlcHJvY2Vzc2luZyBsaWtlIHNjYWxpbmcgdmFyaWFibGVzIGFuZCByZW1vdmluZyByZWR1ZGFudCB2YXJpYWJsZXMuCgo0KSBNb2RlbCBzcGVjaWZpY2F0aW9uLCBmaXR0aW5nLCB0dW5pbmcgYW5kIHBlcmZvcm1hbmNlIGV2YWx1YXRpb24gdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEKClRoZSBtb2RlbCBuZWVkcyB0byBmaXJzdCBiZSBmaXQgdG8gdGhlIHRyYWluaW5nIGRhdGEuIEZpcnN0IHRoZSBtZXRob2Qgb3IgYWxnb3JpdGhtIGluIHdoaWNoIHRoZSBtb2RlbCB3aWxsIGJlIGZpdCBpcyBzcGVjaWZpZWQgKHJlZ3Jlc3Npb24sIHJhbmRvbSBmb3Jlc3RzIGV0Yy4pLiAgVGhlbiBpbiBib3RoIGNsYXNzaWZpY2F0aW9uIGFuZCBwcmVkaWN0aW9uLCB0aGUgbW9kZWwgaXMgZml0IHRvIHRoZSB0cmFpbmcgZGF0YSBhbmQgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBhcmUgdXNlZCB0byBlc3RpbWF0ZSBudW1lcmljIHZhbHVlcyAoaW4gdGhlIGNhc2Ugb2YgcHJlZGljdGlvbikgb3IgY2F0ZWdvcmljYWwgdmFsdWVzIChpbiB0aGUgY2FzZSBvZiBjbGFzc2lmaWNhdGlvbikgb2YgdGhlIG91dGNvbWUgdmFyaWFibGUgb2YgaW50ZXJlc3QuIElmIHRoZSBtb2RlbCBmaXRzIHdlbGwgdGhhbiB0aGVzZSBlc3RpbWF0ZWQgdmFsdWVzIHdpbGwgYmUgdmVyeSBzaW1pbGFyIHRvIHRoZSB0cnVlIG91dGNvbWUgdmFyaWFibGUgdmFsdWVzLiBJZiB0aGUgbW9kZWwgZG9lcyBub3QgZml0IHdlbGwsIHRoYW4gdGhlc2UgZXN0aW1hdGVzIHdpbGwgYmUgbW9yZSBkaXNzaW1hbCBmcm9tIHRoZSB0cnVlIG91dGNvbWUgdmFyaWFibGUuIEluIHRoaXMgY2FzZSwgYXNwZWN0cyBhYm91dCB0aGUgbW9kZWwgbWF5IG5lZWQgdG8gYmUgbW9kaWZpZWQgdG8gaW1wcm92ZSB0aGUgc2ltaWxhcml0eSBvZiB0aGUgZXN0aW1hdGVzIHdpdGggdGhhdCBvZnRoZSB0cnVlIG91dGNvbWUgdmFsdWVzLiBPbmUgd2F5IHRvIG9wdGltaXplIG1vZGVsIHBlcmZvcm1hbmNlIGlzIGEgcHJvY2VzcyBjYWxsZWQgdHVuaW5nIGluIHdoaWNoIGRpZmZlcmVudCBtb2RlbCBbaHlwZXJwYXJhbWV0ZXJdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb21tdW5pdHkvdHV0b3JpYWxzL3BhcmFtZXRlci1vcHRpbWl6YXRpb24tbWFjaGluZS1sZWFybmluZy1tb2RlbHMpe3RhcmdldD0iX2JsYW5rIn0gb3B0aW9ucyBhcmUgdGVzdGVkIHRvIGRldGVybWluZSB0aGUgYmVzdCBvcHRpb24gZm9yIG1vZGVsIHBlcmZvcm1hbmNlLgoKNSkgT3ZlcmFsbCBtb2RlbCBwZXJmb3JtYW5jZSBldmFsdWF0aW9uCgpNb2RlbCBwZXJmb3JtYW5jZSBpcyBhc3Nlc3NlZCBhcyB0aGUgc2ltaWxhcml0eSBiZXR3ZWVuIHRoZSBlc3RpbWF0ZXMgb2YgdGhlIG91dGNvbWUgdmFyaWFibGUgcHJvZHVjZWQgYnkgdGhlIG1vZGVsIGFuZCB0aGUgdHJ1ZSBvdXRjb21lIHZhcmlhYmxlIHZhbHVlcy4gVGhpcyBpcyBkb25lIHR5cGljYWxseSBhcyBhbiBpbnRlcmF0aXZlIHByb2Nlc3Mgd2l0aCB0aGUgdHJhaW5pbmcgZGF0YSBhbG9uZyBzaWRlIG1vZGlmaWNhdGlvbiBvZiB0aGUgbW9kZWwgdW50aWxsIHRoZSBwZXJmb3JtYW5jZSB1c2luZyB0aGUgdHJhaW5pbmcgZGF0YSBpcyBzYXRpc2ZhY3RvcnkuIEF0IHRoaXMgcG9pbnQsIHRoZSBmaW5hbCBtb2RlbCBwZXJmb3JtYW5jZSBpcyBhc3Nlc3NlZCB1c2luZyB0aGUgdGVzdGluZyBkYXRhLiBUaGlzIHRoZW4gZ2l2ZXMgYW4gZXN0aW1hdGUgYWJvdXQgaG93IHdlbGwgdGhlIG1vZGVsIHdpbGwgcHJlZGljdCBvciBjbGFzc2lmeSB0aGUgb3V0Y29tZSB2YXJpYWJsZSBvZiBpbnRlcmVzdCB3aXRoIG5ldyBpbmRlcGVuZGVudCBkYXRhLiBJZGVhbGx5IG9uZSB3b3VsZCBhbHNvIHBlcmZvcm0gYW4gZXZhbHVhdGlvbiB3aXRoIGluZGVwZW5kZW50IGRhdGEgdG8gcHJvdmlkZSBhIHNlbnNlIG9mIGhvdyBnZW5lcmFsaXphYmxlIHRoZSBtb2RlbCBpcyB0byBvdGhlciBkYXRhIHNvdXJjZXMuCgojIyMgVGhlIHRpZHltb2RlbHMgZWNvc3lzdGVtCgpUbyBwZXJmb3JtIG91ciBhbmFseXNpcyB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBgdGlkeW1vZGVsc2Agc3VpdGUgb2YgcGFja2FnZXMuIFlvdSBtYXkgYmUgZmFtaWxpYXIgd2l0aCB0aGUgb2xkZXIgcGFja2FnZXMgYGNhcmV0YCBvciBgbWxyYCB3aGljaCBhcmUgYWxzbyBmb3IgbWFjaGluZSBsZWFybmluZyBhbmQgbW9kZWxpbmcgYnV0IGFyZSBub3QgYSBwYXJ0IG9mIHRoZSBgdGlkeXZlcnNlYC4gW01heCBLdWhuXShodHRwczovL3Jlc291cmNlcy5yc3R1ZGlvLmNvbS9hdXRob3JzL21heC1rdWhuKXt0YXJnZXQ9Il9ibGFuayJ9IGRlc2NyaWJlcyBgdGlkeW1vZGVsc2AgbGlrZSB0aGlzOgoKPiAiT3RoZXIgcGFja2FnZXMsIHN1Y2ggYXMgY2FyZXQgYW5kIG1sciwgaGVscCB0byBzb2x2ZSB0aGUgUiBtb2RlbCBBUEkgaXNzdWUuIFRoZXNlIHBhY2thZ2VzIGRvIGEgbG90IG9mIG90aGVyIHRoaW5ncyB0b286IHByZXByb2Nlc3NpbmcsIG1vZGVsIHR1bmluZywgcmVzYW1wbGluZywgZmVhdHVyZSBzZWxlY3Rpb24sIGVuc2VtYmxpbmcsIGFuZCBzbyBvbi4gSW4gdGhlIHRpZHl2ZXJzZSwgd2Ugc3RyaXZlIHRvIG1ha2Ugb3VyIHBhY2thZ2VzIG1vZHVsYXIgYW5kIHBhcnNuaXAgaXMgZGVzaWduZWQgb25seSB0byBzb2x2ZSB0aGUgaW50ZXJmYWNlIGlzc3VlLiBJdCBpcyBub3QgZGVzaWduZWQgdG8gYmUgYSBkcm9wLWluIHJlcGxhY2VtZW50IGZvciBjYXJldC4KVGhlIHRpZHltb2RlbHMgcGFja2FnZSBjb2xsZWN0aW9uLCB3aGljaCBpbmNsdWRlcyBwYXJzbmlwLCBoYXMgb3RoZXIgcGFja2FnZXMgZm9yIG1hbnkgb2YgdGhlc2UgdGFza3MsIGFuZCB0aGV5IGFyZSBkZXNpZ25lZCB0byB3b3JrIHRvZ2V0aGVyLiBXZSBhcmUgd29ya2luZyB0b3dhcmRzIGhpZ2hlci1sZXZlbCBBUElzIHRoYXQgY2FuIHJlcGxpY2F0ZSBhbmQgZXh0ZW5kIHdoYXQgdGhlIGN1cnJlbnQgbW9kZWwgcGFja2FnZXMgY2FuIGRvLiIKClRoZXJlIGFyZSBtYW55IHBhY2thZ2VzIGluIHRoZSB0aWR5bW9kZWxzIGVjb3N5c3RlbSB3aGljaCBhc3Npc3Qgd2l0aCB0aGUgdmFyaW91cyBzdGVwcyBvZiB0aGUgbWFjaGluZSBsZWFybmluZyBwcm9jZXNzOgoKYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iODAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJzaW1wbGV0aWR5bW9kZWxzLnBuZyIpKQpgYGAKCgpUaGlzIGlzIGEgZGVwaWN0aW9uIG9mIGhvdyB0aGVzZSB0b29scyBoZWxwIHBlcmZvcm0gdGhlIG92ZXJhbGwgbWFjaGluZSBsZWFybmluZyBwcm9jZXNzOgoKYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iODAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJNYWNoaW5lTGVhcm5pbmcucG5nIikpCmBgYAoKIyMjIFRoZSBtYWpvciBiZW5lZml0cyBvZiB0aWR5bW9kZWxzICAKCjEpIFN0YW5kYXJkaXplZCB3b3JrZmxvdy9mb3JtYXQvbm90YXRpb24gYWNyb3NzIGRpZmZlcmVudCB0eXBlcyBvZiBhbGdvcml0aG1zICAKCkRpZmZlcmVudCBub3RhdGlvbnMgYXJlIHJlcXVpcmVkIGZvciBkaWZmZXJlbnQgYWxnb3JpdGhtcyBhcyB0aGUgYWxnb3JpdGhtcyBoYXZlIGJlZW4gZGV2ZWxvcGVkIGJ5IG1hbnkgZGlmZmVyZW50IHBlb3BsZS4gVGhpcyB3b3VsZCByZXF1aXJlIHRoZSBwYWluc3Rha2luZyBwcm9jZXNzIG9mIHJlZm9ybWF0dGluZyB0aGUgZGF0YSB0byBiZSBjb21wYXRpYmxlIHdpdGggZWFjaCBhbGdvcml0aG0gaWYgbXVsdGlwbGUgYWxnb3JpdGhtcyB3ZXJlIHRlc3RlZC4KCjIpIENhbiBlYXNpbHkgbW9kaWZ5IHByZXByb2Nlc3NpbmcsIGFsZ29yaXRobSBjaG9pY2UsIGFuZCBoeXBlci1wYXJhbWV0ZXIgdHVuaW5nIG1ha2luZyBvcHRpbWl6YXRpb24gZWFzeSAgCgpNb2RpeWluZyBhIHBpZWNlIG9mIHRoZSBvdmVyYWxsIHByb2Nlc3MgaXMgbm93IG11Y2ggZWFzaWVyIHRoYW4gYmVmb3JlIGJlY2F1c2UgbWFueSBvZiB0aGUgc3RlcHMgYXJlIHNwZWNpZmllZCB1c2luZyB0aGUgdGlkeW1vZGVsIHBhY2thZ2VzIGluIGEgY29udmVuaWVudCBtYW5uZXIuIFRodXMgdGhlIGVudGlyZSBwcm9jZXNzIGNhbiBiZSByZXJ1biBhZnRlciBhIHNpbXBsZSBjaGFuZ2UgdG8gcHJlcHJvY2Vzc2luZyB3aXRob3V0IG11Y2ggZGlmZmljdWx0eS4KCiMjIyBTcGxpdHRpbmcgdGhlIERhdGEKClRoZSBmaXJzdCBzdGVwIGFmdGVyIGRhdGEgZXhwbG9yYXRpb24gaW4gbWFjaGluZSBsZWFybmluZyBhbmFseXNpcyBpcyB0byBbc3BsaXQgdGhlIGRhdGFdKGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS90cmFpbi12YWxpZGF0aW9uLWFuZC10ZXN0LXNldHMtNzJjYjQwY2JhOWU3KXt0YXJnZXQ9Il9ibGFuayJ9IGludG8gKip0cmFpbmluZyoqIGFuZCAqKnRlc3RpbmcqKiBkYXRhc2V0cy4gCgpUaGUgdHJhaW5pbmcgZGF0YXNldCB3aWxsIGJlIHVzZWQgdG8gYnVpbGQgYW5kIHR1bmUgb3VyIG1vZGVsLiBUaGlzIGlzIHRoZSBkYXRhIHRoYXQgdGhlIG1vZGVsICJsZWFybnMiIG9uLgoKVGhlIHRlc3Rpbmcgc2V0IHdpbGwgYmUgdXNlZCB0byBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2Ygb3VyIG1vZGVsIGluIGEgbW9yZSBnZW5lcmFsaXphYmxlIHdheS4gV2hhdCBkbyB3ZSBtZWFuIGJ5ICJnZW5lcmFsaXphYmxlIj8KClJlbWVtYmVyIHRoYXQgb3VyIG1haW4gZ29hbCBpcyB0byB1c2Ugb3VyIG1vZGVsIHRvIGJlIGFibGUgdG8gcHJlZGljdCBhaXIgcG9sbHV0aW9uIGxldmVscyBpbiBhcmVhcyB3aGVyZSB0aGVyZSBhcmUgbm8gZ3JhdmltZXRyaWMgbW9uaXRvcnMuIFRoZXJlZm9yZSwgaWYgb3VyIG1vZGVsIGlzIHN1cGVyIGdvb2QgYXQgcHJlZGljdGluZyBhaXIgcG9sbHV0aW9uIHdpdGggdGhlIGRhdGEgdGhhdCB3ZSB1c2UgdG8gYnVpbGQgaXQsIGl0IG1pZ2h0IG5vdCBkbyB0aGUgYmVzdCBqb2IgZm9yIHRoZSBhcmVhcyB3aGVyZSB0aGVyZSBhcmUgZmV3IHRvIG5vIG1vbml0b3JzLiBUaGlzIHdvdWxkIGNhdXNlIHVzIHRvIGhhdmUgcmVhbGx5IGdvb2QgcHJlZGljdGlvbiBhY2N1cmFjeSBhbmQgd2UgbWlnaHQgYXNzdW1lIHRoYXQgd2Ugd2VyZSBnb2luZyB0byBkbyBhIGdvb2Qgam9iIGVzdGltYXRpbmcgYWlyIHBvbGx1dGlvbiBhbnkgdGltZSB3ZSB1c2Ugb3VyIG1vZGVsLCBidXQgaW4gZmFjdCB0aGlzIHdvdWxkIGxpa2VseSBub3QgYmUgdGhlIGNhc2UuIFRoaXMgc2l0dWF0aW9uIGlzIHdoYXQgd2UgY2FsbCAqKltvdmVyZml0dGluZ10oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3RyYWluLXRlc3Qtc3BsaXQtYW5kLWNyb3NzLXZhbGlkYXRpb24taW4tcHl0aG9uLTgwYjYxYmVjYTRiNil7dGFyZ2V0PSJfYmxhbmsifSAqKi4KCk92ZXJmaXR0aW5nIGhhcHBlbnMgd2hlbiB3ZSBlbmQgdXAgbW9kZWxpbmcgbm90IG9ubHkgdGhlIG1ham9yIHJlbGF0aW9uc2hpcHMgaW4gb3VyIGRhdGEgYnV0IGFsc28gdGhlIG5vaXNlIHdpdGhpbiBvdXIgZGF0YS4gCgoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL21pcm8ubWVkaXVtLmNvbS9tYXgvMTExMC8xKnRCRXJYWVZ2VHcyalNVWUs3dGhVMkEucG5nIikKYGBgCgojIyMjIyBbW3NvdXJjZV0oaHR0cHM6Ly9taXJvLm1lZGl1bS5jb20vbWF4LzExMTAvMSp0QkVyWFlWdlR3MmpTVVlLN3RoVTJBLnBuZyldCgpJZiB3ZSBnZXQgZmFpcmx5IGdvb2QgcHJlZGljdGlvbiB3aXRoIG91ciB0ZXN0aW5nIHNldCB0aGVuIHdlIHdpbGwga25vdyB0aGF0IG91ciBtb2RlbCBjYW4gYmUgYXBwbGllZCB0byBvdGhlciBkYXRhIGFuZCB3aWxsIHBlcmZvcm0gZmFpcmx5IHdlbGwuIFdlIHdpbGwgZGlzY3VzcyB0aGlzIG1vcmUgbGF0ZXIuCgpXZSB3aWxsIG5vdCB0b3VjaCB0aGUgdGVzdGluZyBzZXQgdW50aWwgd2UgaGF2ZSBjb21wbGV0ZWQgb3B0aW1pemluZyBvdXIgbW9kZWwgd2l0aCB0aGUgdHJhaW5pbmcgc2V0LiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gaGF2ZSBhIGxlc3MgYmlhc2VkIGV2YWx1YXRpb24gb2YgaG93IHdlbGwgb3VyIG1vZGVsIGNhbiBkbyB3aXRoIG90aGVyIGRhdGEgYmVzaWRlcyB0aGUgZGF0YSB1c2VkIGluIHRoZSB0cmFpbmluZyBzZXQgdG8gYnVpbGQgdGhlIG1vZGVsLiBJZGVhbGx5IHlvdSB3b3VsZCBhbHNvIHdhbnQgYSBjb21wbGV0ZWx5IGluZGVwZW5kZW50IGRhdGFzZXQgdG8gZnVydGhlciB0ZXN0IHRoZSBwZXJmb3JtYW5jZSBvZiB5b3VyIG1vZGVsLgoKW0hlcmVdKGh0dHBzOi8vbWFjaGluZWxlYXJuaW5nbWFzdGVyeS5jb20vZGlmZmVyZW5jZS10ZXN0LXZhbGlkYXRpb24tZGF0YXNldHMvKXt0YXJnZXQ9Il9ibGFuayJ9IGlzIGEgZ3JlYXQgZGVzY3JpcHRpb24gb2YgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGVzdGluZyBhbmQgdHJhaW5pbmcgZGF0YXNldHMuCgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI0MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsInNwbGl0LnBuZyIpKQpgYGAKV2Ugd2lsbCB1c2UgdGhlIGByc2FtcGxlYCBwYWNrYWdlIHRvIHBlcmZvcm0gdGhpcyBzdGVwLgoKVGhlYGluaXRpYWxfc3BsaXQoKWAgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIHNwZWNpZnkgaG93IHdlIHdhbnQgdG8gc3BsaXQgb3VyIGRhdGEuIFR5cGljYWxseSBkYXRhIGlzIHNwbGl0IGludG8gMy80IGZvciB0cmFpbmluZyBhbmQgMS80IGZvciB0ZXN0aW5nLlRoaXMgaXMgdGhlIGRlZmF1bHQgcHJvcG9ydGlvbiBhbmQgZG9lcyBub3QgbmVlZCB0byBiZSBzcGVjaWZpZWQuIEhvd2V2ZXIgeW91IGNhbiBjaGFuZ2UgdGhlIHByb3BvcnRpb24gdXNpbmcgdGhlIGBwcm9wYCBhcmd1bWVudCwgd2hpY2ggd2Ugd2lsbCBkbyB0aGF0IGhlcmUgZm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3Nlcy4gWW91IGNhbiBhbHNvIHNwZWNpZnkgYSB2YXJpYWJsZSB0byBzdHJhdGlmeSBieSB3aXRoIHRoZSBgc3RyYXRhYCBhcmd1bWVudC4gVGhpcyBpcyB1c2VmdWwgaWYgeW91IGhhdmUgaW1iYWxhbmNlZCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIHlvdSB3b3VsZCBsaWtlIHRvIGludGVudGlvbmFsbHkgbWFrZSBzdXJlIHRoYXQgdGhlcmUgYXJlIHNpbWlsYXIgbnVtYmVyIG9mIHNhbXBsZXMgb2YgdGhlIHJhcmVyIGNhdGVnb3JpZXMgaW4gYm90aCB0aGUgdGVzdGluZyBhbmQgdHJhaW5pbmcgc2V0cy4gT3RoZXJ3aXNlIHRoZSBzcGxpdCBpcyBwZXJmb3JtZWQgcmFuZG9tbHkuIAoKPiBUaGUgc3RyYXRhIGFyZ3VtZW50IGNhdXNlcyB0aGUgcmFuZG9tIHNhbXBsaW5nIHRvIGJlIGNvbmR1Y3RlZCB3aXRoaW4gdGhlIHN0cmF0aWZpY2F0aW9uIHZhcmlhYmxlLiBUaGUgY2FuIGhlbHAgZW5zdXJlIHRoYXQgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBpbiB0aGUgdHJhaW5pbmcgZGF0YSBpcyBlcXVpdmFsZW50IHRvIHRoZSBwcm9wb3J0aW9ucyBpbiB0aGUgb3JpZ2luYWwgZGF0YSBzZXQuCgpJbiB0aGUgY2FzZSB3aXRoIG91ciBkYXRhc2V0LCBwZXJoYXBzIHdlIHdvdWxkIGxpa2Ugb3VyIHRyYWluaW5nIHNldCB0byBoYXZlIHNpbWlsYXIgcHJvcG9ydGlvbnMgb2YgbW9uaXRvcnMgZnJvbSBlYWNoIG9mIHRoZSBzdGF0ZXMgYXMgaW4gdGhlIGluaXRpYWwgZGF0YS4gVGhpcyBtaWdodCBiZSB1c2VmdWwgaWYgd2Ugd2FudCBvdXIgbW9kZWwgdG8gYmUgZ2VuZXJhbGl6YWJsZSBhY3Jvc3MgYWxsIG9mIHRoZSBzdGF0ZXMuCgpXZSBjYW4gc2VlIHRoYXQgaW5kZWVkIHRoZXJlIGFyZSBkaWZmZXJlbnQgcHJvcG9ydGlvbnMgb2YgbW9uaXRvcnMgaW4gZWFjaCBzdGF0ZSBieSB1c2luZyB0aGUgYGNvdW50KClgIGZ1bmN0aW9uIG9mIHRoZSBgZHB5cmAgcGFja2FnZS4gCgojIyMjIHsuc2Nyb2xsYWJsZSB9CmBgYHtyfQojIFNjcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhCmNvdW50KHBtLCBzdGF0ZSkgJT4lCiAgcHJpbnQobiA9IDFlMykKYGBgCiMjIyMKCklmIG91ciBkYXRhc2V0IHdlcmUgbGFyZ2UgZW5vdWdoIGl0IG1pZ2h0IGJlIG5pY2UgdGhlbiB0byBzdHJhdGlmeSBieSBzdGF0ZSwgYnV0IG91ciBkYXRhIGlzIHVuZm9ydHVuYXRlbHkgbm90IGxhcmdlIGVub3VnaC4gV2Ugd2lsbCBzaG93IGhvdyBvbmUgd291bGQgZG8gdGhpcyB0aG91Z2ggZm9yIGlsbHVzdHJhdGl2ZSBwdXJwb3Nlcy4gVGhpcyBvcHRpb24gaXMgb2Z0ZW4gbW9yZSBpbXBvcnRhbnQgZm9yIGNsYXNzaWZpY2F0aW9uIGFwcGxpY2F0aW9ucyBvZiBtYWNoaW5lIGxlYXJuaW5nIHRoYW4gaXQgaXMgZm9yIHByZWRpY3Rpb24uCgpTaW5jZSB0aGUgc3BsaXQgaXMgcGVyZm9ybWVkIHJhbmRvbWx5LCBpdCBpcyBhIGdvb2QgaWRlYSB0byB1c2UgdGhlIGBzZXQuc2VlZCgpYCBiYXNlIGZ1bmN0aW9uIHRvIGVuc3VyZSB0aGF0IGlmIHlvdXIgcmVydW4geW91ciBjb2RlIHRoYXQgeW91ciBzcGxpdCB3aWxsIGJlIHRoZSBzYW1lIG5leHQgdGltZS4gV2UgY2FuIHNlZSB0aGUgbnVtYmVyIG9mIG1vbml0b3JzIGluIG91ciB0cmFpbmluZywgdGVzdGluZywgYW5kIG9yaWdpbmFsIGRhdGEgYnkgdHlwaW5nIGluIHRoZSBuYW1lIG9mIG91ciBzcGxpdCBvYmplY3QuIFRoZSByZXN1bHQgd2lsbCBsb29rIGxpa2UgdGhpczoKPHRyYWluaW5nIGRhdGEgc2FtcGxlIG51bWJlciwgdGVzdGluZyBkYXRhIHNhbXBsZSBudW1iZXIsIG9yaWdpbmFsIHNhbXBsZSBudW1iZXI+IAoKYGBge3J9CnNldC5zZWVkKDEyMzQpCnBtX3NwbGl0IDwtcnNhbXBsZTo6aW5pdGlhbF9zcGxpdChkYXRhID0gcG0sIHByb3AgPSAyLzMpCnBtX3NwbGl0CgojIElmIHN0cmF0aWZ5aW5nOgojIHBtX3NwbGl0X3N0cmF0YSA8LXJzYW1wbGU6OmluaXRpYWxfc3BsaXQoZGF0YSA9IHBtLCBwcm9wID0gMi8zLCBzdHJhdGEgPSAic3RhdGUiKQoKYGBgCgpJbXBvcnRhbnRseSB0aGUgYGluaXRpYWxfc3BsaXRgIGZ1bmN0aW9uIG9ubHkgZGV0ZXJtaW5lcyB3aGF0IHJvd3Mgb2Ygb3VyIHBtIGRhdGEgZnJhbWUgc2hvdWxkIGJlIGFzc2lnbmVkIGZvciB0cmFpbmluZyBvciB0ZXN0aW5nLCBpdCBkb2VzIG5vdCBhY3R1YWxseSBzcGxpdCB0aGUgZGF0YS4gCgpUbyBleHRyYWN0IHRoZSB0ZXN0aW5nIGFuZCB0cmFpbmluZyBkYXRhIHdlIGNhbiB1c2UgdGhlIGB0cmFpbmluZygpYCBhbmQgYHRlc3RpbmcoKWAgZnVuY3Rpb25zIGFsc28gb2YgdGhlIGByc2FtcGxlYCBwYWNrYWdlLgoKIyMjIyB7LnNjcm9sbGFibGUgfQpgYGB7cn0KIHRyYWluX3BtIDwtcnNhbXBsZTo6dHJhaW5pbmcocG1fc3BsaXQpCiB0ZXN0X3BtIDwtcnNhbXBsZTo6dGVzdGluZyhwbV9zcGxpdCkKIAojIFNjcm9sbCB0aHJvdWdoIHRoZSBvdXRwdXQhCmNvdW50KHRyYWluX3BtLCBzdGF0ZSkKY291bnQodGVzdF9wbSwgc3RhdGUpCmBgYAojIyMjCgoKCiMjIyBWYXJpYWJsZSBSb2xlIEFzc2lnbm1lbnQgYW5kIFByZXByb2Nlc3NpbmcKCkluIHRpZHltb2RlbHMgd2Ugd2lsbCBjcmVhdGUgYSByZWNpcGUsIHdoaWNoIGlzIGEgc3RhbmRhcmRpemVkIGZvcm1hdCBmb3IgYSBzZXF1ZW5jZSBvZiBzdGVwcyBmb3IgcHJvY2Vzc2luZyB0aGUgZGF0YS4KClRoaXMgY2FuIGJlIHZlcnkgdXNlZnVsIGJlY2F1c2UgaXQgbWFrZXMgdGVzdGluZyBvdXQgZGlmZmVyZW50IHByZS1wcm9jZXNzaW5nIHN0ZXBzIG9yIGRpZmZlcmVudCBhbGdvcml0aG1zIHdpdGggdGhlIHNhbWUgcHJlLXByb2Nlc3NpbmcgdmVyeSBlYXN5IGFuZCByZXByb2R1Y2libGUuCgoqKkNyZWF0aW5nIGEgcmVjaXBlIHNwZWNpZmllcyBob3cgYSBkYXRhZnJhbWUgb2YgcHJlZGljdG9ycyBzaG91bGQgYmUgY3JlYXRlZCAgLSBpdCBzcGVjaWZpZXMgd2hhdCAgdmFyaWFibGVzIHRvIGJlIHVzZWQgIGFuZCB0aGUgIHByZXByb2Nlc3Npbmcgc3RlcHMgIGJ1dCBpdCBkb2VzIG5vdCBleGVjdXRlIHRoZXNlIHN0ZXBzIG9yIGNyZWF0ZSB0aGUgZGF0YWZyYW1lIG9mIHByZWRpY3RvcnMuKioKCiMjIyMgTGlzdCB0aGUgaW5ncmVkaWVudHMgLyBzcGVjaWZ5IHRoZSB2YXJpYWJsZXMgd2l0aCB0aGUgYHJlY2lwZSgpYCBmdW5jdGlvbgoKVGhlIGZpcnN0IHRoaW5nIHRvIGRvIHRvIGNyZWF0ZSBhIHJlY2lwZSBpcyB0byBzcGVjaWZ5IHdoaWNoIHZhcmlhYmxlcyB3ZSB3aWxsIGJlIHVzaW5nIGFzIG91ciBvdXRjb21lIGFuZCBwcmVkaWN0b3JzIHVzaW5nIHRoZSBgcmVjaXBlKClgIGZ1bmN0aW9uLiBJbiB0ZXJtcyBvZiB0aGUgbWV0YXBob3Igb2YgYmFraW5nLCB3ZSBjYW4gdGhpbmsgb2YgdGhpcyBhcyBsaXN0aW5nIG91ciBpbmdyZWRpZW50cy4gVGhlIG5hbWluZyBjb252ZW50aW9uIGZvciByZWNpcGUgb2JqZWN0IG5hbWVzIGlzIGAqX3JlY2Agb3IgYHJlY2AuIAoKYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNDAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJTdGFydGluZ19hX3JlY2lwZV9yZWNpcGVzMS5wbmciKSkKYGBgCgoKSW4gb3VyIGNhc2UgcmVjYWxsIHRoYXQgb3VyIGB2YWx1ZWAgdmFyaWFibGUsIHdoaWNoIGlzIHRoZSBhdmVyYWdlIGFubnVhbCBncmF2aW1ldHJpYyBtb25pdG9yIFBNfjIuNX4gY29uY2VudHJhdGlvbiBpbiB1Zy9tXjNeLiBPdXIgcHJlZGljdG9ycyBhcmUgYWxsIHRoZSBvdGhlciB2YXJpYWJsZXMgZXhjZXB0IHRoZSBtb25pdG9yIElELCB3aGljaCBpcyBhbiBgaWRgIHZhcmlhYmxlLgoKVGhlIHJlYXNvbiBub3QgdG8gaW5jbHVkZSB0aGlzIHZhcmlhYmxlIGlzIGJlY2F1c2UgdGhpcyB2YXJpYWJsZSBpbmNsdWRlcyB0aGUgY291bnR5IG51bWJlciBhbmQgYSBudW1iZXIgZGVzaWduYXRpbmcgd2hpY2ggcGFydGljdWxhciBtb25pdG9yIHRoZSB2YWx1ZXMgY2FtZSBmcm9tIG9mIHRoZSBtb25pdG9ycyB0aGVyZSBhcmUgaW4gdGhhdCBjb3VudHkuIFNpbmNlIHRoaXMgbnVtYmVyIGlzIGFyYml0cmFyeSBhbmQgdGhlIGNvdW50eSBpbmZvcm1hdGlvbiBpcyBhbHNvIGdpdmVuIGluIHRoZSBkYXRhLCBhbmQgdGhlIGZhY3QgdGhhdCBlYWNoIG1vbml0b3Igb25seSBoYXMgb25lIHZhbHVlIGluIHRoZSBgdmFsdWVgIHZhcmlhYmxlLCBub3RoaW5nIGlzIGdhaW5lZCBieSBpbmNsdWRpbmcgdGhpcyB2YXJpYWJsZSBhbmQgaXQgbWF5IGluc3RlYWQgaW50cm9kdWNlIG5vaXNlLiBIb3dldmVyLCBpdCBpcyB1c2VmdWwgdG8ga2VlcCB0aGlzIGRhdGEgdG8gdGFrZSBhIGxvb2sgYXQgd2hhdCBpcyBoYXBwZW5pbmcgbGF0ZXIuIFdlIHdpbGwgc2hvdyB5b3Ugd2hhdCB0byBkbyBpbiB0aGlzIGNhc2UgaW4ganVzdCBhIGJpdC4KClRoZSBzaW1wbGVzdCByZWNpcGUgd2l0aCBubyBwcmVwcm9jZXNzaW5nIHN0ZXBzLCB3b3VsZCBiZSB0byBzaW1wbHkgbGlzdCB0aGUgb3V0Y29tZSBhbmQgcHJlZGljdG9yIHZhcmlhYmxlcy4KCldlIGNhbiBkbyBzbyBpbiB0d28gd2F5czogIAoKMSkgVXNpbmcgZm9ybXVsYSBub3RhdGlvbiAgCjIpIEFzc2lnbmluZyByb2xlcyB0byBlYWNoIHZhcmlhYmxlICAKCkxldCdzIGxvb2sgYXQgdGhlIGZpcnN0IHdheSB1c2luZyBmb3JtdWxhIG5vdGF0aW9uLCB3aGljaCBsb29rcyBsaWtlIHRoaXM6ICAKCm91dGNvbWUocykgfiBwcmVkaWN0b3IocykgIAoKSWYgaW4gdGhlIGNhc2Ugb2YgbXVsdGlwbGUgcHJlZGljdG9ycyBvciBhIG11bHRpdmFyaWF0ZSBzaXR1YXRpb24gd2l0aCB0d28gb3V0Y29tZXMsIHVzZSBhIHBsdXMgc2lnbiAgCgpvdXRjb21lMSArIG91dGNvbWUyIH4gcHJlZGljdG9yMSArIHByZWRpY3RvcjIgIAoKSWYgd2Ugd2FudCB0byBpbmNsdWRlIGFsbCBwcmVkaWN0b3JzIHdlIGNhbiB1c2UgYSBwZXJpb2QgbGlrZSBzbzogIAoKb3V0Y29tZV92YXJpYWJsZV9uYW1lIH4gLiAgCgpOb3cgd2l0aCBvdXIgZGF0YSB3ZSB3aWxsIHN0YXJ0IGJ5IG1ha2luZyBhIHJlY2lwZSBmb3Igb3VyIHRyYWluaW5nIGRhdGEuIEluIHRoZSBzaW1wbGVzdCBjYXNlIHdlIG1pZ2h0IHVzZSBhbGwgcHJlZGljdG9ycyBsaWtlIHRoaXM6CgpgYGB7cn0KCnNpbXBsZV9yZWMgPC10cmFpbl9wbSAlPiUKICByZWNpcGVzOjpyZWNpcGUodmFsdWUgfiAuKQoKc2ltcGxlX3JlYwpgYGAKCgpIb3dldmVyLCB0byBkZWFsIHdpdGggdGhlIGlkIHZhcmlhYmxlIHdlIGNvdWxkIHVzZSB0aGUgYHVwZGF0ZV9yb2xlKClgIGZ1bmN0aW9uIG9mIHRoZSBgcmVjaXBlc2AgcGFja2FnZS4gVGhpcyBvcHRpb24gd29ya3Mgd2VsbCB3aXRoIHRoZSBuZXdlciBgd29ya2Zsb3dzYCBwYWNrYWdlLCBob3dldmVyIGlkIHZhcmlhYmVscyBhcmUgb2Z0ZW4gZHJvcHBlZCBmcm9tIGFuYWx5c2VzIHRoYXQgZG8gbm90IHVzZSB0aGlzIG5ld2VyIHBhY2thZ2UgYXMgdGhleSBjYW4gbWFrZSB0aGUgcHJvY2VzcyBkaWZmaWN1bHQgd2l0aCB1c2luZyB0aGUgYHBhcnNuaXBgIHBhY2thZ2UgYWxvbmUgZHVlIHRvIHRoZSBmYWN0IHRoYXQgbmV3IGxldmVscyAob3IgcG9zc2libGUgdmFsdWVzKSBtYXkgYmUgaW50cm9kdWNlZCB3aXRoIHRoZSB0ZXN0aW5nIGRhdGEuCgpgYGB7cn0KCnNpbXBsZV9yZWMgPC10cmFpbl9wbSAlPiUKICByZWNpcGVzOjpyZWNpcGUodmFsdWUgfiAuKSAlPiUKICByZWNpcGVzOjp1cGRhdGVfcm9sZShpZCwgbmV3X3JvbGUgPSAiaWQgdmFyaWFibGUiKQoKc2ltcGxlX3JlYwpgYGAKCldlIGNvdWxkIGFsc28gc3BlY2lmeSB0aGUgb3V0Y29tZSBhbmQgcHJlZGljdG9ycyBpbiB0aGUgc2FtZSB3YXkgYXMgdGhlIGlkIHZhcmlhYmxlLiBQbGVhc2Ugc2VlIFtoZXJlXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JlY2lwZXMvcmVmZXJlbmNlL3JlY2lwZS5odG1sKSBmb3IgZXhhbXBsZXMgb2Ygb3RoZXIgcm9sZXMgZm9yIHZhcmlhYmxlcy4gVGhlIHJvbGUgY2FuIGJlIGFjdHVhbGx5IGJlIGFueSB2YWx1ZS4gCgpUaGUgb3JkZXIgaXMgaW1wb3J0YW50IGhlcmUsIGFzIHdlIGZpcnN0IG1ha2UgYWxsIHZhcmlhYmxlcyBwcmVkaWN0b3JzIGFuZCB0aGVuIG92ZXJyaWRlIHRoaXMgcm9sZSBmb3IgdGhlIG91dGNvbWUgYW5kIGlkIHZhcmlhYmxlLiBXZSB3aWxsIHVzZSB0aGUgYGV2ZXJ5dGhpbmcoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byBzdGFydCB3aXRoIGFsbCBvZiB0aGUgdmFyaWFibGVzIGluIGB0cmFpbl9wbWAuCgpgYGB7cn0KCnNpbXBsZV9yZWMgPC1yZWNpcGUodHJhaW5fcG0pICU+JQogICAgdXBkYXRlX3JvbGUoZXZlcnl0aGluZygpLCBuZXdfcm9sZSA9ICJwcmVkaWN0b3IiKSU+JQogICAgdXBkYXRlX3JvbGUodmFsdWUsIG5ld19yb2xlID0gIm91dGNvbWUiKSU+JQogICAgdXBkYXRlX3JvbGUoaWQsIG5ld19yb2xlID0gImlkIHZhcmlhYmxlIikKCnNpbXBsZV9yZWMKCmBgYAoKSWYgd2Ugd2FudCB0byB0YWtlIGEgbG9vayBhdCBvdXIgZm9ybXVsYSBmcm9tIG91ciByZWNpcGUgd2UgY2FuIGRvIHVzZSB0aGUgYGZvcm11bGEoKWAgZnVuY3Rpb24gb2YgdGhlIGBzdGF0c2AgcGFja2FnZS4KCmBgYHtyfQpmb3JtdWxhKHNpbXBsZV9yZWMpCmBgYAoKV2UgY2FuIGFsc28gdmlldyBvdXIgcmVjaXBlIGluIG1vcmUgZGV0YWlsIHVzaW5nIHRoZSBiYXNlIGBzdW1tYXJ5KClgIGZ1bmN0aW9uLgoKYGBge3J9CnN1bW1hcnkoc2ltcGxlX3JlYykKYGBgCgojIyMjIExpc3QgdGhlIHByZXByb2Nlc3Npbmcgc3RlcHMgdXNpbmcgdGhlIHN0ZXAgZnVuY3Rpb25zIG9mIHRoZSBgcmVjaXBlYCBwYWNrYWdlCgpUaGUgb3RoZXIgdGhpbmcgdGhlIHJlY2lwZXMgcGFja2FnZSBhbGxvd3MgZm9yIGlzIHNwZWNpZnlpbmcgcHJlLXByb2Nlc3Npbmcgc3RlcHMgdXNpbmcgYSB2YXJpZXR5IG9mIGBzdGVwKigpYCBmdW5jdGlvbnMuCgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI0MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsIk1ha2luZ19hX3JlY2lwZV9yZWNpcGVzMi5wbmciKSkKYGBgCgoKKipUaGlzIFtsaW5rXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JlY2lwZXMvcmVmZXJlbmNlL2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gYW5kIHRoaXMgW2xpbmtdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yZWNpcGVzL3JlY2lwZXMucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IHNob3cgdGhlIG1hbnkgb3B0aW9ucyBmb3IgcmVjaXBlIHN0ZXAgZnVuY3Rpb25zLioqCgo8dT5UaGVyZSBhcmUgc3RlcCBmdW5jdGlvbnMgZm9yIGEgdmFyaWV0eSBvZiBwdXJwb3Nlczo8L3U+CgoxKSBbKipJbXB1dGF0aW9uKipdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ltcHV0YXRpb25fKHN0YXRpc3RpY3MpKXt0YXJnZXQ9Il9ibGFuayJ9ICAtLSB3aGljaCBtZWFucyBmaWxsaW5nIGluIG1pc3NpbmcgdmFsdWVzIGJhc2VkIG9uIHRoZSBleGlzdGluZyBkYXRhIAoyKSBbKipUcmFuc2Zvcm1hdGlvbioqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EYXRhX3RyYW5zZm9ybWF0aW9uXyhzdGF0aXN0aWNzKSl7dGFyZ2V0PSJfYmxhbmsifSAgLS0gd2hpY2ggbWVhbnMgY2hhbmdpbmcgYWxsIHZhbHVlcyBvZiBhIHZhcmlhYmxlIGluIHRoZSBzYW1lIHdheSwgdHlwaWNhbGx5IHRvIG1ha2UgaXQgbW9yZSBub3JtYWwgb3IgZWFzaWVyIHRvIGludGVycHJldCkgIAozKSBbKipEaXNjcmV0aXphdGlvbioqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EaXNjcmV0aXphdGlvbl9vZl9jb250aW51b3VzX2ZlYXR1cmVzKSAtLSB3aGljaCBtZWFucyBjb252ZXJ0aW5nIGNvbnRpbnVvdXMgdmFsdWVzIGludG8gZGlzY3JldGUgb3Igbm9taW5hbCB2YWx1ZXMgLSBiaW5uaW5nIGZvciBleGFtcGxlIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIHBvc3NpYmxlIGxldmVscykgIChIb3dldmVyIHRoaXMgaXMgZ2VuZXJhbGx5IG5vdCBhZHZpc2FibGUhKQo0KSBbKipFbmNvZGluZyAvIENyZWF0aW5nIER1bW15IFZhcmlhYmxlcyoqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EdW1teV92YXJpYWJsZV8oc3RhdGlzdGljcykpIC0tIHdoaWNoIG1lYW5zIGNyZWF0aW5nIGEgbnVtZXJpYyBjb2RlIGZvciBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKWyoqTW9yZSBvbiBEdW1teSBWYXJpYWJsZXMgYW5kIG9uZSBob3QgZW5jb2RpbmcqKl0oaHR0cHM6Ly9tZWRpdW0uY29tL3AvYjU4NDBiZTNjNDFhL3Jlc3BvbnNlcy9zaG93KQo1KSBbKipEYXRhIHR5cGUgY29udmVyc2lvbnMqKl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2hhYmxhci92aWduZXR0ZXMvY29udmVydC5odG1sKSAtLSB3aGljaCBtZWFucyBjaGFuZ2luZyBmcm9tIGludGVnZXIgdG8gZmFjdG9yIG9yIG51bWVyaWMgdG8gZGF0ZSBldGMuCjYpIFsqKkludGVyYWN0aW9uKipdKGh0dHBzOi8vc3RhdGlzdGljc2J5amltLmNvbS9yZWdyZXNzaW9uL2ludGVyYWN0aW9uLWVmZmVjdHMvKSB0ZXJtIGFkZGl0aW9uIHRvIHRoZSBtb2RlbCAtLSB3aGljaCBtZWFucyB0aGF0IHdlIHdvdWxkIGJlIG1vZGVsaW5nIGZvciBwcmVkaWN0b3JzIHRoYXQgd291bGQgaW5mbHVlbmNlIHRoZSBjYXBhY2l0eSBvZiBlYWNoIG90aGVyIHRvIHByZWRpY3QgdGhlIG91dGNvbWUKNykgWyoqTm9ybWFsaXphdGlvbioqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Ob3JtYWxpemF0aW9uXyhzdGF0aXN0aWNzKSkgLS0gd2hpY2ggbWVhbnMgY2VudGVyaW5nIGFuZCBzY2FsaW5nIHRoZSBkYXRhIHRvIGEgc2ltaWxhciByYW5nZSBvZiB2YWx1ZXMKOCkgWyoqRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uLyBTaWduYWwgRXh0cmFjdGlvbioqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9EaW1lbnNpb25hbGl0eV9yZWR1Y3Rpb24pIC0tIHdoaWNoIG1lYW5zIG1hdGhlbWF0aWNhbGx5IG9idGFpbmluZyBhIG5ldyBzbWFsbGVyIHNldCBvZiB2YXJpYWJsZXMgdGhhdCBjYXB0dXJlIHRoZSB2YXJpYXRpb24gb3Igc2lnbmFsIGluIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgKGV4LiBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIGFuZCBJbmRlcGVuZGVudCBDb21wb25lbnQgQW5hbHlzaXMpCjkpICoqRmlsdGVyaW5nKiogLS0gRmlsdGVyaW5nIG9wdGlvbnMgZm9yIHJlbW92aW5nIHZhcmlhYmxlcyAoZXguIHJlbW92ZSB2YXJpYWJsZXMgdGhhdCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gb3RoZXJzIG9yIHJlbW92ZSB2YXJpYWJsZXMgd2l0aCB2ZXJ5IGxpdHRsZSB2YXJpYW5jZSBhbmQgdGhlcmVmb3JlIGxpa2VseSBsaXR0bGUgcHJlZGljdGl2ZSBjYXBhY2l0eSkKMTApIFsqKlJvdyBvcGVyYXRpb25zKipdKGh0dHBzOi8vdGFydGFydXMub3JnL2dhcmV0aC9tYXRocy9MaW5lYXJfQWxnZWJyYS9yb3dfb3BlcmF0aW9ucy5wZGYpIC0tIHdoaWNoIG1lYW5zIHBlcmZvcm1pbmcgZnVuY3Rpb25zIG9uIHRoZSB2YWx1ZXMgd2l0aGluIHRoZSByb3dzICAoZXguIHJlYXJyYW5naW5nLCBmaWx0ZXJpbmcsIGltcHV0aW5nKQoxMSkgKipDaGVja2luZyBmdW5jdGlvbnMqKiAtLSBTYW5pdHkgY2hlY2tzIHRvIGxvb2sgZm9yIG1pc3NpbmcgdmFsdWVzLCB0byBsb29rIGF0IHRoZSB2YXJpYWJsZSBjbGFzc2VzIGV0Yy4KCkFsbCBvZiB0aGUgc3RlcCBmdW5jdGlvbnMgbG9vayBsaWtlIGBzdGVwXypgIGV4Y2VwdCBmb3IgdGhlIGNoZWNrIGZ1bmN0aW9ucyB3aGljaCBsb29rIGxpa2UgYGNoZWNrXypgLgoKVGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byBzZWxlY3Qgd2hhdCB2YXJpYWJsZXMgdG8gYXBwbHkgc3RlcHMgdG86ICAKMSkgdGlkeXNlbGVjdCBtZXRob2RzOiBgY29udGFpbnMoKWAsIGBtYXRjaGVzKClgLCBgc3RhcnRzX3dpdGgoKWAsIGBlbmRzX3dpdGgoKWAsIGBldmVyeXRoaW5nKClgLCBgbnVtX3JhbmdlKClgICAKMikgYmFzZWQgb24gdGhlIHR5cGU6IGBhbGxfbm9taW5hbCgpYCwgYGFsbF9udW1lcmljKClgICwgYGhhc190eXBlKClgIAozKSBiYXNlZCBvbiB0aGUgcm9sZTogYGFsbF9wcmVkaWN0b3JzKClgLCBgYWxsX291dGNvbWVzKClgLCBgaGFzX3JvbGUoKWAKNCkgbmFtZSAtIHVzZSB0aGUgYWN0dWFsIG5hbWUgb2YgdGhlIHZhcmlhYmxlL3ZhcmlhYmxlcyBvZiBpbnRlcmVzdCAgCgoKTGV0J3MgdHJ5IGFkZGluZyBzb21lIHN0ZXBzIHRvIG91ciByZWNpcGUuCgpXZSBtaWdodCBjb25zaWRlciBsb2cgdHJhbnNmb3JtaW5nIG91ciBwb3B1bGF0aW9uIGFuZCBhcmVhIHZhcmlhYmxlcyAodGhhdCBhcmVuJ3QgZGVuc2l0aWVzKSAtIGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSByYW5nZSBvZiB0aGVzZSB2YXJpYWJsZXMuCmBgYHtyfQpwbSAlPiUKICBzZWxlY3QobWF0Y2hlcygiX3BvcHxfYXJlYSIpKSAlPiUKICBtYXAocmFuZ2UpCmBgYApXZSBjYW4gc2VlIHRoYXQgdGhlIHJhbmdlIGZvciBlYWNoIG9mIHRoZXNlIHZhcmlhYmxlcyBpcyBxdWl0ZSBsYXJnZSwgd2UgY2FuIGxvZyB0cmFuc2Zvcm0gdGhpcyBkYXRhIHVzaW5nIHRoZSBgc3RlcF9sb2coKWAgZnVuY3Rpb24gb2YgdGhlIGByZWNpcGVzYCBwYWNrYWdlLgoKV2Ugd291bGQgYWxzbyB3YW50IHRvIHBvdGVudGlhbGx5IG9uZSBob3QgZW5jb2RlIHNvbWUgb2Ygb3VyIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBzbyB0aGF0IHRoZXkgY2FuIGJlIHVzZWQgd2l0aCBjZXJ0YWluIGFsZ29yaXRobXMuIFdlIGNhbiBkbyB0aGlzIHdpdGggdGhlIGBzdGVwX2R1bW15KClgIGZ1bmN0aW9uIGFuZCB0aGUgYG9uZV9ob3QgPSBUUlVFYCBhcmd1bWVudC4gT3VyIGZpcHMgdmFyaWFibGUgaW5jbHVkZXMgYSBudW1lcmljIGNvZGUgZm9yIHN0YXRlIGFuZCBjb3VudHkgLSBhbmQgdGhlcmVmb3JlIGlzIGVzc2VudGlhbGx5IGEgcHJveHkgZm9yIGNvdW50eS4gIFNpbmNlIHdlIGFscmVhZHkgaGF2ZSBjb3VudHksIHdlIHdpbGwganVzdCB1c2UgaXQgYW5kIGtlZXAgdGhlIGZpcHMgaWQgYXMgYW5vdGhlciBJRCB2YXJpYWJsZS4KCldlIGNhbiByZW1vdmUgdGhlIGBmaXBzYCB2YXJpYWJsZSBmcm9tIHRoZSBwcmVkaWN0b3JzIHVzaW5nIGB1cGRhdGVfcm9sZSgpYCB0byBtYWtlIHN1cmUgdGhhdCB0aGUgcm9sZSBpcyBubyBsb25nZXIgYCJwcmVkaWN0b3IiYC4gV2UgY2FuIG1ha2UgdGhlIHJvbGUgYW55dGhpbmcgd2Ugd2FudCBhY3R1YWxseSwgc28gd2Ugd2lsbCBrZWVwIGl0IHNvbWV0aGluZyBpZGVudGlmaWFibGUuCgpXZSBtaWdodCBhbHNvIHdhbnQgdG8gcmVtb3ZlIHZhcmlhYmxlcyB0aGF0IGFwcGVhciB0byBiZSByZWR1bmRhbnQgYW5kIGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIG90aGVycywgYXMgd2Uga25vdyBmcm9tIG91ciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIHRoYXQgbWFueSBvZiBvdXIgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIHdpdGggb25lIGFub3RoZXIuIFdlIGNhbiBkbyB0aGlzIHVzaW5nIHRoZSBgc3RlcF9jb3JyKClgIGZ1bmN0aW9uLgoKSXQgaXMgYWxzbyBhIGdvb2QgaWRlYSB0byByZW1vdmUgdmFyaWFibGVzIHdpdGggbmVhci16ZXJvIHZhcmlhbmNlLCB3aGljaCBjYW4gYmUgZG9uZSB3aXRoIHRoZSBgc3RlcF9uenYoKWAgZnVuY3Rpb24uIFZhcmlhYmxlcyBoYXZlIGxvdyB2YXJpYW5jZSBpZiBhbGwgdGhlIHZhbHVlcyBhcmUgdmVyeSBzaW1pbGFyLCB0aGUgdmFsdWVzIGFyZSB2ZXJ5IHNwYXJzZSwgb3IgaWYgdGhleSBhcmUgaGlnaGx5IGltYmFsYW5jZWQuIAoKRXhhbXBsZXMgd2hlcmUgeW91IG1pZ2h0IGhhdmUgbmVhci16ZXJvIHZhcmlhbmNlIHZhcmlhYmxlcyBpbmNsdWRlOiAjIyMgSSBTdHJ1Z2dsZWQgdG8gZmluZCBsaW5rcyBmb3IgdGhpcy4uLiB0byBiYWNrIHVwIG15IHN0YXRlbWVudHMjIyMKCjEpICoqU2ltaWxhciBWYWx1ZXMqKiAtIElmIHRoZSBwb3B1bGF0aW9uIGRlbnNpdHkgd2FzIG5lYXJseSB0aGUgc2FtZSBmb3IgZXZlcnkgemN0YSB0aGF0IGNvbnRhaW5lZCBhIG1vbml0b3IsIHRoZW4ga25vd2luZyB0aGUgcG9wdWxhdGlvbiBkZW5zaXR5IG5lYXIgb3VyIG1vbml0b3J3b3VsZCBjb250cmlidXRlIGxpdHRsZSB0byBvdXIgbW9kZWwgaW4gYXNzaXN0aW5nIHVzIHRvIHByZWRpY3QgbW9uaXRvciBhaXIgcG9sbHV0aW9uIHZhbHVlcy4gCjIpICoqU3BhcnNlIERhdGEqKiAtIElmIGFsbCBvZiB0aGUgbW9uaXRvcnMgd2VyZSBpbiBsb2NhdGlvbnMgd2hlcmUgdGhlIHBvcHVsYXRpb25zIGRpZCBub3QgYXR0ZW5kIGdyYWR1YXRlIHNjaG9vbCwgdGhlbiB0aGVzZSB2YWx1ZXMgd291bGQgbW9zdGx5IGJlIHplcm8sIGFnYWluIHRoaXMgd291bGQgZG8gdmVyeSBsaXR0bGUgdG8gaGVscCB1cyBkaXN0aW5ndWlzaCBvdXIgYWlyIHBvbGx1dGlvbiBtb25pdG9ycy5XaGVuIG1hbnkgb2YgdGhlIHZhbHVlcyBhcmUgemVybyB0aGlzIGlzIGFsc28gY2FsbGVkIHNwYXJzZSBkYXRhLiAgCjMpICoqSW1iYWxhbmNlZCBEYXRhKiogSWYgbmVhcmx5IGFsbCBvZiB0aGUgbW9uaXRvcnMgd2VyZSBsb2NhdGVkIGluIG9uZSBwYXJ0aWN1bGFyIHN0YXRlLCBhbmQgYWxsIHRoZSBvdGhlcnMgb25seSBoYWQgb25lIG1vbml0b3IgZWFjaCwgdGhlbiB0aGUgcmVhbCBwcmVkaWN0aXZlIHZhbHVlIHdvdWxkIHNpbXBseSBiZSBpbiBrbm93aW5nIGlmIGEgbW9uaXRvciBpcyBsb2NhdGVkIGluIHRoYXQgcGFydGljdWxhciBzdGF0ZSBvciBub3QuIEluIHRoaXMgY2FzZSB3ZSBkb24ndCB3YW50IHRvIHJlbW92ZSBvdXIgdmFyaWFibGUsIHdlIGp1c3Qgd2FudCB0byBzaW1wbGlmeSBpdC4KCioqSXQgaXMgaW1wb3J0YW50IHRvIGFkZCB0aGUgc3RlcHMgdG8gdGhlIHJlY2lwZSBpbiBhbiBvcmRlciB0aGF0IG1ha2VzIHNlbnNlIGp1c3QgbGlrZSB3aXRoIGEgY29va2luZyByZWNpcGUuKioKClRodXMgZmlyc3Qgd2UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBudW1lcmljIHZhbHVlcyBmb3Igb3VyIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgdGhlbiB3ZSB3aWxsIGxvb2sgYXQgY29ycmVsYXRpb24gYW5kIG5lYXItemVybyB2YXJpYW5jZS4gV2UgZG9uJ3Qgd2FudCB0byByZW1vdmUgc29tZSBvZiBvdXIgdmFyaWFibGVzLCBsaWtlIHRoZSBDTUFRIGFuZCBhb2QgdmFyaWFibGVzIHNvIHdlIGNhbiBtYWtlIHN1cmUgdGhleSBhcmUga2VwdCBpbiB0aGUgbW9kZWwgYnkgZXhjbHVkaW5nIHRoZW0gZnJvbSB0aG9zZSBzdGVwcy4gSWYgd2Ugc3BlY2lmaWNhbGx5IHdhbnRlZCB0byByZW1vdmUgYSBwcmVkaWN0b3Igd2UgY291bGQgdXNlIGBzdGVwX3JtKClgLgoKYGBge3J9CnNpbXBsZV9yZWMgJTw+JQogIHVwZGF0ZV9yb2xlKCJmaXBzIiwgbmV3X3JvbGUgPSAiY291bnR5IGlkIikgJT4lCiMgY3JlYXRlIG51bWVyaWMgZHVtbXkgdmFyaWFibGVzIHRvIGVuY29kZSBmb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzCiAgc3RlcF9kdW1teShzdGF0ZSwgY291bnR5LCBjaXR5LCB6Y3RhLCBvbmVfaG90ID0gVFJVRSkgJT4lCiAgIyBpZGVudGlmeSBhbmQgcmVvbXZlIGFsbCBjb3JyZWxhdGVkIHByZWRpY3RvcnMgKG5vdyB0aGF0IHRoZXkgYXJlIG51bWVyaWMpCiAgIHN0ZXBfY29ycihhbGxfcHJlZGljdG9ycygpLCAtIENNQVEsIC0gYW9kKSU+JQogICMgaWRlbnRpZnkgdmFyaWFibGVzIHdpdGggbmVhciB6ZXJvIHZhcmlhbmNlIGFuZCByZW1vdmUKICBzdGVwX256dihhbGxfcHJlZGljdG9ycygpLCAtIENNQVEsIC0gYW9kKQogIApzaW1wbGVfcmVjCgpgYGAKCgoKIyMjIFJ1bm5pbmcgdGhlIHByZXByb2Nlc3NpbmcKClRoZSBuZXh0IG1ham9yIGZ1bmN0aW9uIG9mIHRoZSBgcmVjaXBlc2AgcGFja2FnZSBpcyBgcHJlcCgpYC4KClRoaXMgZnVuY3Rpb24gdXBkYXRlcyB0aGUgcmVjaXBlIG9iamVjdCBiYXNlZCBvbiB0aGUgdHJhaW5pbmcgZGF0YS4gSXQgZXN0aW1hdGVzIHBhcmFtZXRlcnMgKGVzdGltYXRpbmcgdGhlIHJlcXVpcmVkIHF1YW50aXRpZXMgYW5kIHN0YXRpc3RpY3MgcmVxdWlyZWQgYnkgdGhlIHN0ZXBzIGZvciB0aGUgdmFyaWFibGVzKSBmb3IgcHJlcHJvY2Vzc2luZyBhbmQgIHVwZGF0ZXMgdGhlIG1vZGVsIHRlcm1zLCBhcyBzb21lIG9mIHRoZSBwcmVkaWN0b3JzIG1heSBiZSByZW1vdmVkLCB0aGlzIGFsbG93cyB0aGUgcmVjaXBlIHRvIGJlIHJlYWR5IHRvIHVzZSBvbiBvdGhlciBkYXRhc2V0cy4gSXQgZG9lc24ndCBuZWNlc3NhcmlseSBhY3R1YWxseSBleGVjdXRlIHRoZSBwcmVwcm9jZXNzaW5nIGl0c2VsZiwgaG93ZXZlciB3ZSB3aWxsIHNwZWNpZnkgaW4gYXJndW1lbnQgZm9yIGl0IHRvIGRvIHRoaXMgc28gdGhhdCB3ZSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIHByZXByb2Nlc3NlZCBkYXRhLgoKVGhlcmUgYXJlIHNvbWUgaW1wb3J0YW50IGFyZ3VtZW50cyB0byBrbm93IGFib3V0OgoxKSB0cmFpbmluZyAtIHlvdSBtdXN0IHN1cHBseSBhIHRyYWluaW5nIGRhdGEgc2V0IHRvIGVzdGltYXRlIHBhcmFtZXRlcnMgZm9yIHByZXByb2Nlc3Npbmcgb3BlcmF0aW9ucyAocmVjaXBlIHN0ZXBzKSAtIHRoaXMgbWF5IGFscmVhZHkgYmUgaW5jbHVkZWQgaW4geW91ciByZWNpcGUgLSBhcyBpcyB0aGUgY2FzZSBmb3IgdXMKMikgZnJlc2ggLSBpZiBUUlVFIC0gd2lsbCByZXRyYWluIGFuZCBlc3RpbWF0ZSBwYXJhbWV0ZXJzIGZvciBhbnkgcHJldmlvdXMgc3RlcHMgdGhhdCB3ZXJlIGFscmVhZHkgcHJlcHBlZCBpZiB5b3UgYWRkIG1vcmUgc3RlcHMgdG8gdGhlIHJlY2lwZQozKSB2ZXJib3NlIC0gaWYgYFRSVUVgIHNob3dzIHRoZSBwcm9ncmVzcyBhcyB0aGUgc3RlcHMgYXJlIGV2YWx1YXRlZCBhbmQgdGhlIHNpemUgb2YgdGhlIHByZXByb2Nlc3NlZCB0cmFpbmluZyBzZXQKNCkgcmV0YWluIC0gaWYgYFRSVUVgIHRoZW4gdGhlIHByZXByb2Nlc3NlZCB0cmFpbmluZyBzZXQgd2lsbCBiZSBzYXZlZCB3aXRoaW4gdGhlIHJlY2lwZSAoYXMgdGVtcGxhdGUpLiBUaGlzIGlzIGdvb2QgaWYgeW91IGFyZSBsaWtlbHkgdG8gYWRkIG1vcmUgc3RlcHMgYW5kIGRvbid0IHdhbnQgdG8gcmVydW4gdGhlIGBwcmVwKClgIG9uIHRoZSBwcmV2aW91cyBzdGVwcy4gSG93ZXZlciB0aGlzIGNhbiBtYWtlIHRoZSByZWNpcGUgc2l6ZSBsYXJnZS4gVGhpcyBpcyBuZWNlc3NhcnkgaWYgeW91IHdhbnQgdG8gYWN0dWFsbHkgbG9vayBhdCB0aGUgcHJlcHJvY2Vzc2VkIGRhdGEuCgoKYGBge3J9CnByZXBwZWRfcmVjIDwtIHByZXAoc2ltcGxlX3JlYywgdmVyYm9zZSA9IFRSVUUsIHJldGFpbiA9IFRSVUUgKQpuYW1lcyhwcmVwcGVkX3JlYykKYGBgCgpUaGVyZSBhcmUgYWxzbyBsb3RzIG9mIHVzZWZ1bCB0aGluZ3MgdG8gY2hlY2tvdXQgaW4gdGhlIG91dHB1dCBvZiBgcHJlcCgpYC4KWW91IGNhbiBzZWU6CjEpIHRoZSBgc3RlcHNgIHRoYXQgd2VyZSBydW4gIAoyKSB0aGUgdmFyaWFibGUgaW5mbyAoYHZhcl9pbmZvYCkgIAozKSB0aGUgbW9kZWwgYHRlcm1faW5mb2AKNCkgdGhlIG5ldyBgbGV2ZWxzYCBvZiB0aGUgdmFyaWFibGVzIAo1KSB0aGUgb3JpZ2luYWwgbGV2ZWxzIG9mIHRoZSB2YXJpYWJsZXMgYG9yaWdfbHZsc2AgICAKNikgaW5mbyBhYm91dCB0aGUgdHJhaW5pbmcgZGF0YSBzZXQgc2l6ZSBhbmQgY29tcGxldGVuZXNzIChgdHJfaW5mb2ApCgpOb3RlOiAgWW91IG1heSBzZWUgdGhlIGBwcmVwLnJlY2lwZSgpYCBmdW5jdGlvbiBpbiBtYXRlcmlhbCB0aGF0IHlvdSByZWFkIGFib3V0IHRoZSBgcmVjaXBlc2AgcGFja2FnZS4gVGhpcyBpcyByZWZlcnJpbmcgdG8gdGhlIGBwcmVwKClgIGZ1bmN0aW9uIG9mIHRoZSBgcmVjaXBlc2AgcGFja2FnZS4KCiMjIyMgRXh0cmFjdGluZyB0aGUgcHJlcHJvY2Vzc2VkIHRyYWluaW5nIGRhdGEKCmBgYHtyLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjQwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwidHJhaW5pbmdfcHJlcHJvY2Vzc2luZ19yZWNpcGVzMy5wbmciKSkKYGBgCgpTaW5jZSB3ZSByZXRhaW5lZCBvdXIgcHJlcHJvY2Vzc2VkIHRyYWluaW5nIGRhdGEsIHdlIGNhbiB0YWtlIGEgbG9vayBhdCBpdCBsaWtlIGJ5IHVzaW5nIHRoZSBganVpY2UoKWAgZnVuY3Rpb24gb2YgdGhlIGByZWNpcGVzYCBwYWNrYWdlIGxpa2UgdGhpczoKCiMjIyMgey5zY3JvbGxhYmxlIH0KYGBge3J9CiMgU2Nyb2xsIHRoZXJvdWdoIHRoZSBvdXRwdXQhCmp1aWNlZF90cmFpbjwtIGp1aWNlKHByZXBwZWRfcmVjKQpnbGltcHNlKGp1aWNlZF90cmFpbikKYGBgCiMjIyMKCgpGb3IgZWFzeSBjb21wYXJpc29uIHNha2UgLSBoZXJlIGlzIG91ciBvcmlnaW5hbCBkYXRhOgojIyMjIHsuc2Nyb2xsYWJsZSB9CmBgYHtyfQojIFNjcm9sbCB0aGVyb3VnaCB0aGUgb3V0cHV0IQpnbGltcHNlKHBtKQpgYGAKIyMjIwoKTm90aWNlIGhvdyB3ZSBvbmx5IGhhdmUgMzYgdmFyaWFibGVzIG5vdyBpbnN0ZWFkIG9mIDUwISBUd28gb2YgdGhlc2UgYXJlIG91ciBJRCB2YXJpYWJsZXMgKGBmaXBzYCBhbmQgdGhlIGFjdHVhbCBtb25pdG9yIElEIChgaWRgKSkgYW5kIG9uZSBpcyBvdXIgb3V0Y29tZSAoYHZhbHVlYCkuIFRodXMgd2Ugb25seSBoYXZlIDMzIHByZWRpY3RvcnMgbm93LiBXZSBjYW4gYWxzbyBzZWUgdGhhdCB2YXJpYWJsZXMgdGhhdCB3ZSBubyBsb25nZXIgaGF2ZSBhbnkgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBWYXJpYWJsZXMgbGlrZSBgc3RhdGVgIGFyZSBnb25lIGFuZCBvbmx5IGBzdGF0ZV9DYWxpZm9ybmlhYCByZW1haW5zIGFzIGl0IHdhcyB0aGUgb25seSBzdGF0ZSBpZGVudGl0eSB0byBoYXZlIG5vbnplcm8gdmFyaWFuY2UuICBXZSBjYW4gc2VlIHRoYXQgQ2FsaWZvcm5pYSBoYWQgdGhlIGxhcmdlc3QgbnVtYmVyIG9mIG1vbml0b3JzIGNvbXBhcmVkIHRvIHRoZSBvdGhlciBzdGF0ZXMuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZXJlIHdlcmUgbW9yZSBtb25pdG9ycyBsaXN0ZWQgYXMgYCJOb3QgaW4gYSBjaXR5ImAgdGhhbiBhbnkgY2l0eS4gCgojIyMjIHsuc2Nyb2xsYWJsZSB9CmBgYHtyfQpwbSAlPiUgY291bnQoc3RhdGUpCnBtICU+JSBjb3VudChjaXR5KQpgYGAKIyMjIwoKKipOb3RlKio6ICBSZWNhbGwgdGhhdCB5b3UgbXVzdCBzcGVjaWZ5IGByZXRhaW4gPSBUUlVFYCBhcmd1bWVudCBvZiB0aGUgYHByZXAoKWAgZnVuY3Rpb24gdG8gdXNlIGBqdWljZSgpYC4KCiMjIyMgRXh0cmFjdGluZyB0aGUgcHJlcHJvY2Vzc2VkIHRlc3RpbmcgZGF0YQoKYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNDAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJ0ZXN0aW5nX3ByZXByb2Nlc3NpbmdfcmVjaXBlczQucG5nIikpCmBgYAoKQWNjcm9kaW5nIHRvIHRoZSB0aWR5bW9kZWxzIGRvY3VtZW50YXRpb246Cgo+IGBiYWtlKClgIHRha2VzIGEgdHJhaW5lZCByZWNpcGUgYW5kIGFwcGxpZXMgdGhlIG9wZXJhdGlvbnMgdG8gYSBkYXRhIHNldCB0byBjcmVhdGUgYSBkZXNpZ24gbWF0cml4LgogZm9yIGV4YW1wbGU6ICBpdCBhcHBsaWVzIHRoZSBjZW50ZXJpbmcgdG8gbmV3IGRhdGEgc2V0cyB1c2luZyB0aGVzZSBtZWFucyB1c2VkIHRvIGNyZWF0ZSB0aGUgcmVjaXBlCgoKSWYgeW91IHdhbnRlZCB0byBsb29rIGF0IHRoZSBwcmVwcm9jZXNzZWQgdGVzdGluZyBkYXRhIHlvdSB3b3VsZCB1c2UgdGhlIGBiYWtlKClgIGZ1bmN0aW9uIG9mIHRoZSBgcmVjaXBlc2AgcGFja2FnZS4KKFlvdSBnZW5lcmFsbHkgd2FudCB0byBsZWF2ZSB5b3VyIHRlc3RpbmcgZGF0YSBhbG9uZSwgYnV0IGl0IGlzIGdvb2QgdG8gbG9vayBmb3IgaXNzdWVzIGxpa2UgdGhlIGludHJvZHVjdGlvbiBvZiBOQSB2YWx1ZXMpLgoKIyMjIyB7LnNjcm9sbGFibGUgfQpgYGB7cix9CiMgU2Nyb2xsIHRoZXJvdWdoIHRoZSBvdXRwdXQhCmJha2VkX3Rlc3RfcG0gPC0gcmVjaXBlczo6YmFrZShwcmVwcGVkX3JlYywgbmV3X2RhdGEgPSB0ZXN0X3BtKQpnbGltcHNlKGJha2VkX3Rlc3RfcG0gKQpgYGAKIyMjIwoKCk5vdGljZSB0aGF0IG91ciBgY2l0eV9Ob3QuaW4uYS5jaXR5YCB2YXJpYWJsZSBzZWVtcyB0byBiZSBOQSB2YWx1ZXMuIFdoeSBtaWdodCB0aGF0IGJlPwoKQWghIFBlcnBoYXMgaXQgaXMgYmVjYXVzZSBzb21lIG9mIG91ciBsZXZlbHMgd2VyZSBub3QgcHJldmlvdXNseSBzZWVuIGluIHRoZSB0cmFpbmluZyBzZXQhCgpMZXQncyB0YWtlIGEgbG9vayB1c2luZyB0aGUgW3NldCBvcGVyYXRpb25zXShodHRwczovL3d3dy5wcm9iYWJpbGl0eWNvdXJzZS5jb20vY2hhcHRlcjEvMV8yXzJfc2V0X29wZXJhdGlvbnMucGhwKSBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlLiBXZSBjYW4gdGFrZSBhIGxvb2sgYXQgY2l0aWVzIHRoYXQgd2VyZSBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdGVzdCBhbmQgdHJhaW5pbmcgc2V0LgoKYGBge3J9CnRyYWluY2l0aWVzIDwtIHRyYWluX3BtICU+JSBkaXN0aW5jdChjaXR5KQp0ZXN0Y2l0aWVzIDwtIHRlc3RfcG0gJT4lIGRpc3RpbmN0KGNpdHkpCgojZ2V0IHRoZSBudW1iZXIgb2YgY2l0aWVzIHRoYXQgd2VyZSBkaWZmZXJlbnQKZGltKGRwbHlyOjpzZXRkaWZmKHRyYWluY2l0aWVzLCB0ZXN0Y2l0aWVzKSkKCiNnZXQgdGhlIG51bWJlciBvZiBjaXRpZXMgdGhhdCBvdmVybGFwcGVkCmRpbShkcGx5cjo6aW50ZXJzZWN0KHRyYWluY2l0aWVzLCB0ZXN0Y2l0aWVzKSkKYGBgCgpJbmRlZWQsIHRoZXJlIGFyZSBsb3RzIG9mIGRpZmZlcmVudCBjaXRpZXMgaW4gb3VyIHRlc3QgZGF0YSB0aGF0IGFyZSBub3QgaW4gb3VyIHRyYWluaW5nIGRhdGEhCgpNYXliZSByZW1vdmUgdGhpcz86IFRodXMgd2UgbmVlZCB0byB1cGRhdGUgb3VyIG9yaWdpbmFsIHJlY2lwZSB0byBpbmNsdWRlIGEgdmVyeSBpbXBvcnRhbnQgc3RlcCBmdW5jdGlvbiBjYWxsZWQgYHN0ZXBfbm92ZWwoKWAgdGhpcyBoZWxwcyBpbiBjYXNlcyBsaWtlIHRoaXMgd2VyZSB0aGVyZSBhcmUgbmV3IGZhY3RvcnMgaW4gb3VyIHRlc3Rpbmcgc2V0IHRoYXQgd2VyZSBub3QgaW4gb3VyIHRyYWluaW5nIHNldC4gSXQgaXMgYSBnb29kIGlkZWEgdG8gaW5jbHVkZSB0aGlzIGluIG1vc3Qgb2YgeW91ciByZWNpcGVzIHdoZXJlIHlvdSBoYXZlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdpdGggbWFueSBkaXN0aW5jdCB2YWx1ZXMuIFRoaXMgc3RlcCBuZWVkcyB0byBjb21lIGJlZm9yZSB3ZSBjcmVhdGUgZHVtbXkgdmFyaWFibGVzLiBIb3dldmVyLCB3ZSBhcmUgYWxzbyBjcmVhdGluZyBhIGR1bW15IHZhcmlhYmxlIGZyb20gdGhpcywgd2hpY2ggc3RpbGwgcmVzdWx0cyBpbiBhIHByb2JsZW0uIAoKCkxldCdzIG1vZGlmeSB0aGUgY2l0eSB2YXJpYWJsZSB0byBiZSB2YWx1ZXMgb2YgYGluIGEgY2l0eWAgb3IgYG5vdCBpbiBhIGNpdHlgIHVzaW5nIHRoZSBgaWZfZWxzZSgpYCBmdW5jdGlvbiBvZiBgZHBseXJgLiBBbHRlcm5hdGl2ZWx5IHlvdSBjb3VsZCBjcmVhdGUgYSBbY3VzdG9tIHN0ZXAgZnVuY3Rpb25dKGh0dHBzOi8vcmVjaXBlcy50aWR5bW9kZWxzLm9yZy9hcnRpY2xlcy9DdXN0b21fU3RlcHMuaHRtbCkgdG8gZG8gdGhpcyBhbmQgYWRkIHRoZSBzdGVwIGZ1bmN0aW9uIHRvIHlvdXIgcmVjaXBlLCBidXQgdGhhdCBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgY2FzZSBzdHVkeS4gCgpXZSBuZWVkIHRvIGNyZWF0ZSBhIG5ldyByZWNpcGUgdG8gbW92ZSBmb3J3YXJkLCBhcyB0aGUgbGV2ZWxzIG9mIG91ciB2YXJpYWJsZXMgYXJlIGVzdGFibGlzaGVkIHRoZW4uIFdlIHdvdWxkIGFsc28gcG90ZW50aWFsbHkgaGF2ZSB0aGlzIGlzc3VlIGZvciBzdGF0ZSBhbmQgY291bnR5LiBTbyBsZXQncyBhbHNvIGRvIGEgc2ltaWxhciB0aGluZyBmb3IgYHN0YXRlYC4gVGhlIGBjb3VudHlgIHZhcmlhYmxlcyBhcHBlYXJzIHRvIGdldCBkcm9wcGVkIGR1ZSB0byBlaXRoZXIgY29ycmVsYXRpb24gb3IgbmVhciB6ZXJvIHZhcmlhbmNlLiBJdCBpcyBsaWtlbHkgZHVlIHRvIG5lYXIgemVybyB2YXJpYW5jZSBiZWN1YXNlIHRoaXMgaXMgdGhlIG1vcmUgZ3JhbnVsYXIgb2YgdGhlc2UgZ2VvZ3JhcGhpYyBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIGxpa2VseSBzcGFyc2UuCgoKYGBge3J9CgpwbVtbImNpdHkiXV08LWlmX2Vsc2UoCiAgcG1bWyJjaXR5Il1dID09ICJOb3QgaW4gYSBjaXR5IiwgCiAgdHJ1ZSA9ICJOb3QgaW4gYSBjaXR5IiwgZmFsc2UgPSAiSW4gYSBjaXR5IikKCnBtW1sic3RhdGUiXV0gPC0gaWZfZWxzZSgKICBwbVtbInN0YXRlIl1dID09ICJDYWxpZm9ybmlhIiwgCiAgdHJ1ZSA9ICJDYWxpZm9ybmlhIiwgZmFsc2UgPSAiTm90IENhbGlmb3JuaWEiKQoKc2V0LnNlZWQoMTIzNCkgIyBzYW1lIHNlZWQgYXMgYmVmb3JlCgpwbV9zcGxpdCA8LXJzYW1wbGU6OmluaXRpYWxfc3BsaXQoZGF0YSA9IHBtLCBwcm9wID0gMi8zKQpwbV9zcGxpdAogdHJhaW5fcG0gPC1yc2FtcGxlOjp0cmFpbmluZyhwbV9zcGxpdCkKIHRlc3RfcG0gPC1yc2FtcGxlOjp0ZXN0aW5nKHBtX3NwbGl0KQoKICAKbm92ZWxfcmVjIDwtcmVjaXBlKHRyYWluX3BtKSAlPiUKICAgIHVwZGF0ZV9yb2xlKGV2ZXJ5dGhpbmcoKSwgbmV3X3JvbGUgPSAicHJlZGljdG9yIiklPiUKICAgIHVwZGF0ZV9yb2xlKHZhbHVlLCBuZXdfcm9sZSA9ICJvdXRjb21lIiklPiUKICAgIHVwZGF0ZV9yb2xlKGlkLCBuZXdfcm9sZSA9ICJpZCB2YXJpYWJsZSIpJT4lCiAgICB1cGRhdGVfcm9sZSgiZmlwcyIsIG5ld19yb2xlID0gImNvdW50eSBpZCIpJT4lCiMgY3JlYXRlIG51bWVyaWMgZHVtbXkgdmFyaWFibGVzIHRvIGVuY29kZSBmb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzCiAgICBzdGVwX2R1bW15KHN0YXRlLCBjb3VudHksIGNpdHksIHpjdGEsIG9uZV9ob3QgPSBUUlVFKSAlPiUKIyBpZGVudGlmeSBhbmQgcmVvbXZlIGFsbCBjb3JyZWxhdGVkIHByZWRpY3RvcnMgKG5vdyB0aGF0IHRoZXkgYXJlIG51bWVyaWMpCiAgIHN0ZXBfY29ycihhbGxfbnVtZXJpYygpKSU+JQojIGlkZW50aWZ5IHZhcmlhYmxlcyB3aXRoIG5lYXIgemVybyB2YXJpYW5jZSBhbmQgcmVtb3ZlCiAgc3RlcF9uenYoYWxsX251bWVyaWMoKSkgIyAKIApgYGAKCk5vdyBsZXQncyByZXRyYWluIG91ciB0cmFpbmluZyBkYXRhIGFuZCB0cnkgYmFraW5nIG91ciB0ZXN0IGRhdGE6CgpgYGB7cn0KcHJlcHBlZF9yZWMgPC0gcHJlcChub3ZlbF9yZWMsIHZlcmJvc2UgPSBUUlVFLCByZXRhaW4gPSBUUlVFKQpgYGAKCgojIyMjIHsuc2Nyb2xsYWJsZSB9CmBgYHtyfQojIFNjcm9sbCB0aGVyb3VnaCB0aGUgb3V0cHV0IQpqdWljZWRfdHJhaW48LSBqdWljZShwcmVwcGVkX3JlYykKZ2xpbXBzZShqdWljZWRfdHJhaW4pCmBgYAoKIyMjIwoKTm90aWNlLCBpdCBsb29rcyBsaWtlIHdlIGdhaW5lZCB0aGUgYGxvZ19wcmlzZWNfbGVuZ3RoXzI1MDAwYCBiYWNrIHdpdGggdGhpcyByZWNpcGUgdXNpbmcgdGhlIGRhdGEgd2l0aCBvdXIgY2hhbmdlcyB0byBgc3RhdGVgIGFuZCBgY2l0eWAuCgpgYGB7cn0KYmFrZWRfdGVzdF9wbTwtIHJlY2lwZXM6OmJha2UocHJlcHBlZF9yZWMsIG5ld19kYXRhID0gdGVzdF9wbSkKZ2xpbXBzZShiYWtlZF90ZXN0X3BtKQpgYGAKCkdyZWF0IG5vdyB3ZSBubyBsb25nZXIgaGF2ZSBOQSB2YWx1ZXMhIDopCgoKTm90ZTogaWYgeW91IHVzZSB0aGUgc2tpcCBvcHRpb24gZm9yIHNvbWUgb2YgdGhlIHByZXByb2Nlc3Npbmcgc3RlcHMsIGJlIGNhcmVmdWwuIGBqdWljZSgpYCB3aWxsIHNob3cgYWxsIG9mIHRoZSByZXN1bHRzIGlnbm9yaW5nIGBza2lwID0gVFJVRWAuIGBiYWtlKClgIHdpbGwgbm90IG5lY2Vzc2FyaWx5IGNvbmR1Y3QgdGhlc2Ugc3RlcHMgb24gdGhlIG5ldyBkYXRhLiAKCgojIyMgU3BlY2lmeWluZyB0aGUgTW9kZWwKClNvIGZhciB3ZSBoYXZlIHVzZWQgYHJzYW1wbGVgIHRvIHNwbGl0IHRoZSBkYXRhIGFuZCBgcmVjaXBlc2AgdG8gYXNzaWduIHZhcmlhYmxlIGFuZCB0byBzcGVjaWZ5IGFuZCBwcmVwIG91ciBwcmVwcm9jZXNzaW5nIChhcyB3ZWxsIGFzIHRvIG9wdGlvbmFsbHkgZXh0cmFjdCB0aGUgcHJlcHJvY2Vzc2VkIGRhdGEpLgoKV2Ugd2lsbCBub3cgdXNlIHRoZSBgcGFyc25pcGAgcGFja2FnZSAod2hpY2ggaXMgc2ltaWxhciB0byB0aGUgcHJldmlvdXMgYGNhcmV0YCBwYWNrYWdlIC0gYW5kIGhlbmNlIHdoeSBpdCBpcyBuYW1lZCBhZnRlciB0aGUgdmVnZXRhYmxlKSB0byBzcGVjaWZ5IG91ciBtb2RlbC4KClRoZXJlIGFyZSBmb3VyIGFzcGVjdHMgdG8gZGVmaW5lIGFib3V0IG91ciBtb2RlbDogIAoxKSB0aGUgKip0eXBlKiogb2YgbW9kZWwgKHVzaW5nIHNwZWNpZmljIGZ1bnRpb25zIGluIHBhcnNuaXAgbGlrZSBgcmFuZF9mb3Jlc3QoKWAsIGBsb2dpc3RpY19yZWcoKWAgZXRjLikgIAoyKSB0aGUgKiptb2RlKiogb2YgbGVhcm5pbmcgLSBjbGFzc2lmaWNhdGlvbiBvciByZWdyZXNzaW9uICh1c2luZyB0aGUgYHNldF9tb2RlKClgIGZ1bmN0aW9uKSAgCjMpIHRoZSBwYWNrYWdlIG9yICoqZW5naW5lKiogdGhhdCB3ZSB3aWxsIHVzZSB0byBpbXBsZW1lbnQgdGhlIHR5cGUgb2YgbW9kZWwgc2VsZWN0ZWQgKHVzaW5nIHRoZSBgc2V0X2VuZ2luZSgpYCBmdW5jdGlvbikgIAo0KSBhbnkgKiphcmd1bWVudHMqKiBuZWNlc3NhcnkgZm9yIHRoZSBtb2RlbC9wYWNrYWdlIHNlbGVjdGVkICh1c2luZyB0aGUgYHNldF9hcmdzKClgZnVuY3Rpb24gLSAgZm9yIGV4YW1wbGUgdGhlIGBtdHJ5ID1gIGFyZ3VtZW50IGZvciByYW5kb20gZm9yZXN0IHdoaWNoIGlzIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIHRvIGJlIHVzZWQgYXMgb3B0aW9ucyBmb3Igc3BsaXR0aW5nIGF0IGVhY2ggdHJlZSBub2RlKSAgCgpXZSBhcmUgZ29pbmcgdG8gc3RhcnQgb3VyIGFuYWx5c2lzIHdpdGggYSBsaW5lYXIgcmVncmVzc2lvbiBidXQgd2Ugd2lsbCBkZW1vbnN0cmF0ZSBob3cgd2UgY2FuIHRyeSBkaWZmZXJlbnQgbW9kZWxzLgoKVGhlIGZpcnN0IHRoaW5nIHdlIGRvIGlzIGRlZmluZSB3aGF0IHR5cGUgb2YgbW9kZWwgd2Ugd291bGQgbGlrZSB0byB1c2UuIFNlZSBbaGVyZV0oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby9wYXJzbmlwL2FydGljbGVzL2FydGljbGVzL01vZGVscy5odG1sCil7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9kZWxpbmcgW29wdGlvbnNdaW4gcGFyc25pcC4KCmBgYHtyfQpQTV9tb2RlbCA8LSBwYXJzbmlwOjpsaW5lYXJfcmVnKCkgI1BNIGZvciBwYXJ0aWN1bGF0ZSBtYXRlcgpQTV9tb2RlbApgYGAKCk9LLiBTbyBmYXIgYWxsIHdlIGhhdmUgdG9sZCBgcGFyc25pcGAgaXMgd2Ugd2FudCB0byB1c2UgYSBsaW5lYXIgcmVncmVzc2lvbi4uLiAgTGV0J3MgdGVsbCBgcGFyc25pcGAgbW9yZSBhYm91dCB3aGF0IHdlIHdhbnQuCgpXZSB3b3VsZCBsaWtlIHRvIHVzZSB0aGUgb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyBtZXRob2QgdG8gZml0IG91ciBsaW5lYXIgcmVncmVzc2lvbi4gU28gd2Ugd2lsbCB0ZWxsIGBwYXJzbmlwYCB0aGF0IHdlIHdhbnQgdG8gdXNlIHRoZSBgbG1gIHBhY2thZ2UgdG8gaW1wbGVtZW50IG91ciBsaW5lYXIgcmVncmVzc2lvbiAodGhlcmUgYXJlIG1hbnkgb3B0aW9ucyBhY3R1YWxseS0gc3VjaCBhcyBbYHJzdGFuYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JzdGFuL3ZpZ25ldHRlcy9yc3Rhbi5odG1sKSBbYGdsbW5ldGBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nbG1uZXQvaW5kZXguaHRtbCksIFtga2VyYXNgXShodHRwczovL2tlcmFzLnJzdHVkaW8uY29tLyksIGFuZCBbYHNwYXJrbHlyYF0oaHR0cHM6Ly90aGVyaW5zcGFyay5jb20vc3RhcnRpbmcuaHRtbCNzdGFydGluZy1zcGFya2x5ci1oZWxsby13b3JsZCkpLiBXZSB3aWxsIGRvIHNvIGJ5IHVzaW5nIHRoZSBgc2V0X2VuZ2luZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHBhcnNuaXBgIHBhY2thZ2UuCgpgYGB7cn0KbG1fUE1fbW9kZWwgPC0gCiAgUE1fbW9kZWwgICU+JQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImxtIikKCmxtX1BNX21vZGVsCgpgYGAKCkluIHNvbWUgY2FzZXMgc29tZSBwYWNrYWdlcyBjYW4gZG8gZWl0aGVyIGNsYXNzaWZpY2F0aW9uIG9yIHByZWRpY3Rpb24sIHNvIGl0IGlzIGEgZ29vZCBpZGVhIHRvIHNwZWNpZnkgd2hpY2ggbW9kZSB5b3UgaW50ZW5kIHRvIHBlcmZvcm0uIFlvdSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBgc2V0X21vZGUoKWAgZnVuY3Rpb24gb2YgdGhlIGBwYXJzbmlwYCBwYWNrYWdlLCBieSB1c2luZyBlaXRoZXIgYHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpYCBvciBgc2V0X21vZGUoInJlZ3Jlc3Npb24iKWAuCgpgYGB7cn0KbG1fUE1fbW9kZWwgPC0gCiAgUE1fbW9kZWwgICU+JQogIHBhcnNuaXA6OnNldF9lbmdpbmUoImxtIikgJT4lCiAgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQoKbG1fUE1fbW9kZWwKCmBgYAoKIyMjIEZpdHRpbmcgdGhlIE1vZGVsOiB0d28gd2F5cyAtIGB3b3JrZmxvd3NgIGFuZCBgcGFyc25pcGAKClRvIGZpdCBvdXIgbW9kZWwgd2UgY2FuIHVzZSB0aGUgYHBhcnNuaXBgIHBhY2thZ2UgYW5kIHRoZW4gYXNzZXNzIG91ciBmaXQgdXNpbmcgdGhlIGB5YXJkc3RpY2tgIHBhY2thZ2UuCgpIb3dldmVyIGEgbmV3ZXIgcGFja2FnZSBjYWxsZWQgYHdvcmtmbG93c2AgYWxsb3dzIHVzIHRvIGtlZXAgdHJhY2sgb2YgYm90aCBvdXIgcHJlcHJvY2Vzc2luZyBzdGVwcyBhbmQgb3VyIG1vZGVsIHNwZWNpZmljYXRpb24uIEl0IGFsc28gYWxsb3dzIHVzIHRvIGltcGxlbWVudCBmYW5jaWVyIG9wdGltaXphdGlvbnMgaW4gYW4gYXV0b21hdGVkIHdheSBhbmQgaXQgaXMgY3VycmVudGx5IGJlaW5nIGRldmVsb3BlZCB0byBhbHNvIGhhbmRsZSBwb3N0LXByb2Nlc3Npbmcgb3BlcmF0aW9ucywgc28gaXQgaXMgZ29vZCB0byBsZWFybiBhYm91dCBpdCEKClNvIHdlIHdpbGwgbm93IGNyZWF0ZSBhIHdvcmtmbG93IHdpdGggdGhlIHJlY2lwZSAob3VyIHByZXByb2Nlc3Npbmcgc3BlY2lmaWNhdGlvbnMpIHRoYXQgd2UgbWFkZSBhbmQgdGhlIG1vZGVsIHRoYXQgd2UganVzdCBzcGVjaWZpZWQuCgpGaXJzdCB3ZSB1c2UgdGhlIGB3b3JrZmxvdygpYCBmdW5jdGlvbiBvZiB0aGUgYHdvcmtmbG93c2AgcGFja2FnZSB0byBjcmVhdGUgYSB3b3JrZmxvdy4KClRoZW4gd2UgYWRkIG91ciByZWNpcGUgd2l0aCB0aGUgYGFkZF9yZWNpcGUoKWAgZnVuY3Rpb24gYW5kIHdlIGFkZCBvdXIgbW9kZWwgd2l0aCB0aGUgYGFkZF9tb2RlbCgpYCBmdW5jdGlvbiBvZiB0aGUgYHdvcmtmbG93c2AgcGFja2FnZS4gCgpOb3RlOiBXZSBkbyBub3QgbmVlZCB0byBhY3R1YWxseSBwcmVwIG91ciByZWNpcGUgYmVmb3JlIHVzaW5nIHdvcmtmbG93cyEKCmBgYHtyfQpQTV93ZmxvdyA8LXdvcmtmbG93czo6d29ya2Zsb3coKSAlPiUKICAgICAgICAgICB3b3JrZmxvd3M6OmFkZF9yZWNpcGUobm92ZWxfcmVjKSAlPiUKICAgICAgICAgICB3b3JrZmxvd3M6OmFkZF9tb2RlbChsbV9QTV9tb2RlbCkKUE1fd2Zsb3cKYGBgCgpBaCwgbmljZS4gTm90aWNlIGhvdyBpdCB0ZWxscyB1cyBhYm91dCBib3RoIG91ciBwcmVwcm9jZXNzaW5nIHN0ZXBzIGFuZCBvdXIgbW9kZWwgc3BlY2lmaWNhdGlvbnMuCgpOb3cgd2UgY2FuIHByZXBhcmUgdGhlIHJlY2lwZSAoZXN0aW1hdGUgdGhlIHBhcmFtZXRlcnMpIGFuZCBmaXQgdGhlIG1vZGVsIHRvIG91ciB0cmFpbmluZyBkYXRhIGFsbCBhdCBvbmNlLiBQcmludGluZyB0aGUgb3V0cHV0IHdlIGNhbiBzZWUgdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgbW9kZWwuCgpgYGB7cn0KUE1fd2Zsb3dfZml0IDwtIHBhcnNuaXA6OmZpdChQTV93ZmxvdywgZGF0YSA9IHRyYWluX3BtKQpQTV93Zmxvd19maXQKYGBgCgpPdGhlcndpc2Ugd2UgY291bGQgaGF2ZSBkb25lIHRoaXMgd2l0aG91dCB0aGUgYHdvcmtmbG93c2AgcGFja2FnZS4gTm90aWNlIGhlcmUgd2Ugd2lsbCB1c2VkIHRoZSBwcm9jZXNzZWQgdHJhaW5pbmcgZGF0YSAoYGp1aWNlZF90cmFpbmApIGFzIG9wcG9zZWQgdG8gdGhlIHJhdyB0cmFpbmluZyBkYXRhIHRoYXQgd2UgdXNlZCB3aXRoIHRoZSB3b3JrZmxvdyB3ZSBjcmVhdGVkIHdpdGggYHdvcmtmbG93c2AuCgpJbiB0aGlzIGNhc2UsIHdlIGFjdHVhbGx5IG5lZWQgdG8gd3JpdGUgeW91ciBtb2RlbCBhZ2FpbiEgUmVjYWxsIHRoYXQgYGlkYCBhbmQgYGZpcHNgIGFyZSBJRCB2YXJpYWJsZXMgYW5kIHRoYXQgYHZhbHVlc2AgaXMgb3VyIG91dGNvbWUgb2YgaW50ZXJlc3QgKHRoZSBwbSBhaXIgcG9sbHV0aW9uIG1lYXN1cmUgYXQgZWFjaCBtb25pdG9yKS4gCgpgYGB7cn0KanVpY2VkX3RyYWluX3JlYWR5IDwtIGp1aWNlZF90cmFpbiU+JSBzZWxlY3QoIC1pZCwgLWZpcHMpClBNX2ZpdCA8LSBsbV9QTV9tb2RlbCAlPiUgCnBhcnNuaXA6OmZpdCh2YWx1ZSB+LiwgZGF0YSA9anVpY2VkX3RyYWluX3JlYWR5KQoKYGBgCgojIyMgTG9va2luZyBhdCBtb2RlbCBmaXQgd2l0aCBgYnJvb21gCgpUaGUgYGJyb29tYCBwYWNrYWdlIGFsbG93cyBmb3IgYW4gZWFzeS90aWR5IHdheSB0byBsb29rIGF0IHRoZSBmaXR0ZWQgbW9kZWw6ICAKCmB0aWR5KClgIGdyYWJzIHRoZSBjb2VmZmljaWVudHMgZnJvbSB0aGUgbW9kZWwgIApgZ2xhbmNlKClgIHN1bW1hcml6ZXMgdGhlIG1vZGVsIGZpdCBhbmQgZ2l2ZXMgdXMgYW4gaWRlYSBhYm91dCBob3cgd2VsbCB0aGUgbW9kZWwgbWlnaHQgcGVyZm9ybQpgYXVnbWVudCgpYCBnaXZlcyBhIDE1MCByb3cgb2JzZXJ2YXRpb24gbGV2ZWwgc3VtbWFyeSBvZiB0aGUgZGF0YSBhbmQgZml0IAoKVGhlc2UgYGJyb29tYCBmdW5jdGlvbnMgY3VycmVudGx5IG9ubHkgd29yayB3aXRoIGBwYXJzbmlwYCBvYmplY3RzIG5vdCByYXcgYHdvcmtmbG93c2Agb2JqZWN0cy4gVG8gdXNlIHRoZSBgdGlkeSgpYCBmdW5jdGlvbiB3aXRoIGB3b3JrZmxvd3NgIHdlIG5lZWQgdG8gZmlyc3QgdXNlIHRoZSBgcHVsbF93b3JrZmxvd19maXQoKWAgZnVuY3Rpb24uCgpgYGB7cn0KYnJvb206OnRpZHkoUE1fZml0KSAlPiUgYXJyYW5nZShwLnZhbHVlKQpicm9vbTo6Z2xhbmNlKFBNX2ZpdFtbImZpdCJdXSkKYnJvb206OmF1Z21lbnQoUE1fZml0W1siZml0Il1dKSAjIHRoaXMgYWxzbyBnaXZlcyB1cyB0aGUgZml0dGVkIHZhbHVlcywgc3RhbmRhcmQgZXJyb3IgZm9yIGVhY2ggYW5kIG1vcmUhCgoKd2Zsb3dvdXRwdXQ8LVBNX3dmbG93X2ZpdCAlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUgCiAgYnJvb206OnRpZHkoKSAKCgojVGhlIG91dHB1dCBpcyBpZGVudGljYWwgdXNpbmcgd29ya2Zsb3dzIHRvIGZpdCB0aGUgbW9kZWwgb3IganVzdCBwYXJzbmlwCmlkZW50aWNhbCh0aWR5KFBNX2ZpdCksIHdmbG93b3V0cHV0KQoKYGBgCgoKT0ssIHNvIHdlIGhhdmUgZml0IG91ciBtb2RlbCBvbiBvdXIgdHJhaW5pbmcgZGF0YSwgd2hpY2ggbWVhbnMgd2UgaGF2ZSBjcmVhdGVkIGEgbW9kZWwgdG8gcHJlZGljdCB2YWx1ZXMgb2YgYWlyIHBvbGx1dGlvbiBiYXNlZCBvbiB0aGUgcHJlZGljdG9ycyB0aGF0IHdlIGhhdmUgaW5jbHVkZWQuIFlheSEKCgpXZSBjYW4gZ2V0IGEgc2Vuc2Ugb2YgdGhlIHZhcmlhYmxlIGltcG9ydGFuY2UgdXNpbmcgdGhlIGB2aXAoKWAgZnVuY3Rpb24gb2YgdGhlIGB2aXBgIHBhY2thZ2UuIAoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIHRvcCAxMCBjb250cmlidXRpbmcgdmFyaWFibGVzOgoKYGBge3J9ClBNX3dmbG93X2ZpdCAlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUgCiAgdmlwKG51bV9mZWF0dXJlcyA9IDEwKQpgYGAKCiMjIyBNb2RlbCBQZXJmb3JtYW5jZQoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgaG93IHdlbGwgb3VyIG1vZGVsIGZpdCBvdXIgdHJhaW5pbmcgZGF0YToKCgpgYGB7cn0KIyN1c2luZyB0aGUgcGFyc25pcCB2ZXJzaW9uCiNjYW4gZ3JhYiB0aGUgZml0dGVkIHZhbHVlcyBzaW1wbHkgbGlrZSB0aGlzOgpwYXJzbmlwX2ZpdHRlZF92YWx1ZXM8LWZpdHRlZChQTV9maXRbWyJmaXQiXV0pCgpwYXJzbmlwX2ZpdHRlZF92YWx1ZXMKCiNjYW4gYWxzbyBnZXQgdGhlIHZhbHVlcyB1c2luZyB0aGUgYXVnbWVudCBmdW5jdGlvbgpwYXJzbmlwX2ZpdHRlZF92YWx1ZXMgPC0gYXVnbWVudChQTV9maXRbWyJmaXQiXV0sIGRhdGEgPSBqdWljZWRfdHJhaW4pICU+JSAKc2VsZWN0KHZhbHVlLCAuZml0dGVkOi5zdGQucmVzaWQpCgpwYXJzbmlwX2ZpdHRlZF92YWx1ZXMKCiMjdXNpbmcgdGhlIHdvcmtmbG93cyB2ZXJzaW9uCndmX2ZpdCA8LVBNX3dmbG93X2ZpdCAlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKQoKI2NhbiBncmFiIHRoZSBmaXR0ZWQgdmFsdWVzIHNpbXBseSBsaWtlIHRoaXM6CndmX2ZpdHRlZF92YWx1ZXM8LWZpdHRlZCh3Zl9maXRbWyJmaXQiXV0pCgp3Zl9maXR0ZWRfdmFsdWVzCgojY2FuIGFsc28gZ2V0IHRoZSB2YWx1ZXMgdXNpbmcgdGhlIGF1Z21lbnQgZnVuY3Rpb24Kd2ZfZml0dGVkX3ZhbHVlcyA8LWF1Z21lbnQod2ZfZml0W1siZml0Il1dLCBkYXRhID0ganVpY2VkX3RyYWluKSAlPiUgCnNlbGVjdCh2YWx1ZSwgLmZpdHRlZDouc3RkLnJlc2lkKQoKd2ZfZml0dGVkX3ZhbHVlcwoKI2dpdmVzIHVzIHRoZSBzYW1lIGZpdHRlZCB2YWx1ZXMKaWRlbnRpY2FsKHBhcnNuaXBfZml0dGVkX3ZhbHVlcywgd2ZfZml0dGVkX3ZhbHVlcykKCiMjIExldCdzIG1ha2UgYSBwbG90IG9mIGZpdHRlZCBhbmQgcmVhbCB2YWx1ZXMKCmdncGxvdCh3Zl9maXR0ZWRfdmFsdWVzLCBhZXMoeCA9IC5maXR0ZWQsIHkgPSB2YWx1ZSkpICsgZ2VvbV9wb2ludCgpCmBgYAoKT2ssIHNvIG91ciBmaXR0ZWQgcmFuZ2UgYXBwZWFycyB0byBiZSBzbWFsbGVyIHRoYW4gdGhlIHJlYWwgdmFsdWVzLiBXZSBjb3VsZCBwcm9iYWJseSBkbyBhIGJpdCBiZXR0ZXIuCgoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgaG93IHdlbGwgb3VyIG1vZGVsIHNlZW1zIHRvIGJlIHByZWZvcm1pbmcgbW9yZSBmb3JtYWxseToKCldoZW4gYXNzZXNzaW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBhIG1vZGVsLCB0aGUgbWV0cmljcyB3ZSB1c2UgZGVwZW5kIG9uIGlmIHdlIGFyZSBwcmVmb3JtaW5nIGEgY2xhc3NpZmNhdGlvbiBvciBwcmVkaWN0aW9uIGFsc28ga25vd24gYXMgcmVncmVzc2lvbiBhbmFseXNpcy4gSW4gb3VyIGNhc2Ugd2UgYXJlIHBlcmZvcm1pbmcgYSByZWdyZXNzaW9uIG9yIHByZWRpY3Rpb24gYW5hbHlzaXMgYW5kIHRoZSBtZXRyaWNzIG9mdGVuIHVzZWQgYXJlOgoxKSBtZWFuIGFic29sdXRlIGVycm9yIChtYWUpICAKMikgUiBzcXVhcmVkIGVycm9yIChyc3EpIApUaGlzIGlzIGFsc28ga25vd24gYXMgdGhlIGNvZWZmaWNpZW50IG9mIGRldGVybWluaWF0aW9uIHdoaWNoIGlzIHRoZSBzcXVhcmVkIGNvcnJlbGF0aW9uIGJldHdlZW4gdHJ1dGggYW5kIGVzdGltYXRlICAKMykgcm9vdCBtZWFuIHNxdWFyZWQgZXJyb3IgKHJtc2UpICAgCgoKV2UgY2FuIHVzZSB0aGUgYHlhcmRzdGlja2AgcGFja2FnZSB0byBxdWlja2x5IGNhbGN1bGF0ZSBlc3RpbWF0ZXMgZm9yIGFsbCBvZiB0aGVzZSB2YWx1ZXMgdXNpbmcgdGhlIGBtZXRyaWNzKClgIGZ1bmN0aW9uLiBBbHRlcm5hdGl2ZWx5IGlmIHlvdSBvbmx5IHdhbnRlZCBvbmUgbWV0cmljIHlvdSBjb3VsZCB1c2UgdGhlIGBtYWUoKWAsIGByc3EoKWAsIG9yIGBybXNlKClgIGZ1bmN0aW9ucyByZXNwZWN0aXZlbHkuIFRoaXMgaXMgaGVscGZ1bCB0byBleGFtaW5lIHdpdGggb3VyIGZpdHRlZCB0cmFpbmluZyBzZXQgdmFsdWVzIHRvIHNlZSBob3cgd2VsbCBvdXIgbW9kZWwgaXMgcGVyZm9ybWluZyBhbmQgaWYgd2UgbmVlZCB0byBtYWtlIGFkanVzdG1lbnRzLiAKCmBgYHtyfQoKeWFyZHN0aWNrOjptZXRyaWNzKHdmX2ZpdHRlZF92YWx1ZXMsIHRydXRoID0gdmFsdWUsIGVzdGltYXRlID0gLmZpdHRlZCkKeWFyZHN0aWNrOjptYWUod2ZfZml0dGVkX3ZhbHVlcywgdHJ1dGggPSB2YWx1ZSwgZXN0aW1hdGUgPSAuZml0dGVkKQoKYGBgCgoKIyMjIENyb3NzIHZhbGlkYXRpb24gc2FtcGxlIHNwbGl0dGluZwoKV2Ugd2lsbCB1c2UgdGhlIGByc2FtcGxlYCBwYWNrYWdlIGFnYWluIGluIG9yZGVyIHRvIGZ1cnRoZXIgaW1wbGVtZW50IHdoYXQgYXJlIGNhbGxlZCBbY3Jvc3MgdmFsaWRhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ3Jvc3MtdmFsaWRhdGlvbl8oc3RhdGlzdGljcykpe3RhcmdldD0iX2JsYW5rIn0gdGVjaG5pcXVlcy4gVGhpcyBpcyBhbHNvIGNhbGxlZCAqKnJlc2FtcGxpbmcqKiBvciAqKnJlcGFydGlvbmluZyoqLgpOb3RlOiB3ZSBhcmUgbm90IGFjdHVhbGx5IGdldHRpbmcgbmV3IHNhbXBsZXMgZnJvbSB0aGUgdW5kZXJseWluZyBkaXN0cmlidXRpb24gc28gdGhlIHRlcm0gcmVzYW1wbGluZyBpcyBhIGJpdCBvZiBhIG1pc25vbWVyLgoKW0Nyb3NzIHZhbGlkYXRpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nyb3NzLXZhbGlkYXRpb25fKHN0YXRpc3RpY3MpKXt0YXJnZXQ9Il9ibGFuayJ9IHNwbGl0cyBvdXIgdHJhaW5pbmcgZGF0YSBpbnRvIG11bHRpcGxlIHRyYWluaW5nIGRhdGEgc2V0cyB0byBhbGxvdyBmb3IgYSBkZWVwZXIgYXNzZXNzbWVudCBvZiB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLgoKSGVyZSBpcyBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIGNvbmNlcHQgZm9yIGNyb3NzIHZhbGlkYXRpb24vcmVzYW1wbGluZy9yZXBhcnRpdGlvbmluZyBmcm9tIFtNYXggS3Vobl0oaHR0cHM6Ly9yZXNvdXJjZXMucnN0dWRpby5jb20vYXV0aG9ycy9tYXgta3Vobil7dGFyZ2V0PSJfYmxhbmsifToKCmBgYHtyLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjgwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwicmVzYW1wbGluZy5wbmciKSkKYGBgCgpUZWNobmljYWxseSBjcmVhdGluZyBvdXIgdGVzdGluZyBhbmQgdHJhaW5pbmcgc2V0IG91dCBvZiBvdXIgb3JpZ2luYWwgdHJhaW5pbmcgZGF0YSBpcyBzb21ldGltZXMgY29uc2lkZXJlZCBhIGZvcm0gb2YgW2Nyb3NzIHZhbGlkYXRpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nyb3NzLXZhbGlkYXRpb25fKHN0YXRpc3RpY3MpKXt0YXJnZXQ9Il9ibGFuayJ9IGNhbGxlZCB0aGUgaG9sZG91dCBtZXRob2QuIEFzIHdlIGp1c3QgbGVhcm5lZCB0aGlzIGNhbiBnaXZlIHVzIGEgYmV0dGVyIHNlbnNlIG9mIHRoZSBhY2N1cmFjeSBvZiBvdXIgZGF0YSBpbiBhIG1vcmUgZ2VuZXJhbGl6YWJsZSB3YXkuIAoKSG93ZXZlciwgd2UgY2FuIGRvIGEgYmV0dGVyIGpvYiBvZiBvcHRpbWl6aW5nIG91ciBtb2RlbCBmb3IgYWNjdXJhY3kgaWYgd2UgYWxzbyBwZXJmb3JtIGFub3RoZXIgdHlwZSBvZiBbY3Jvc3MgdmFsaWRhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ3Jvc3MtdmFsaWRhdGlvbl8oc3RhdGlzdGljcykpe3RhcmdldD0iX2JsYW5rIn0gb24gdGhlIG5ld2x5IGRlZmluZWQgdHJhaW5pbmcgc2V0IHRoYXQgd2UganVzdCBjcmVhdGVkLiBUaGVyZSBhcmUgbWFueSBbY3Jvc3MgdmFsaWRhdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ3Jvc3MtdmFsaWRhdGlvbl8oc3RhdGlzdGljcykpe3RhcmdldD0iX2JsYW5rIn0gbWV0aG9kcyBhbmQgbW9zdCBjYW4gYmUgZWFzaWx5IGltcGxlbWVudGVkIHVzaW5nIGByc2FtcGxlc2AgcGFja2FnZS4gV2Ugd2lsbCB1c2UgYSB2ZXJ5IHBvcHVsYXIgbWV0aG9kIGNhbGxlZCBlaXRoZXIgW2stZm9sZCBvciB2LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbl0oaHR0cHM6Ly9tYWNoaW5lbGVhcm5pbmdtYXN0ZXJ5LmNvbS9rLWZvbGQtY3Jvc3MtdmFsaWRhdGlvbi8pe3RhcmdldD0iX2JsYW5rIn0uIAoKVGhpcyBtZXRob2QgaW52b2x2ZXMgZXNzZW50aWFsbHkgcHJlZm9ybWluZyB0aGUgaG9sZCBvdXQgbWV0aG9kIGl0ZXJhdGl2ZWx5IHdpdGggdGhlIHRyYWluaW5nIGRhdGEuIAoKRmlyc3QgdGhlIHRyYWluaW5nIHNldCBpcyBkaXZpZGVkIGludG8gayBvciB2IGVxdWFsbHkgc2l6ZWQgc21hbGxlciBwaWVjZXMuIAoKVGhlbiB0aGUgbW9kZWwgaXMgdHJhaW5lZCBvbiB0aGUgbW9kZWwgb24gay0xIG9yIHYtMSBzdWJzZXRzIG9mIHRoZSBkYXRhIGl0ZXJhdGl2ZWx5IChyZW1vdmluZyBhIGRpZmZlcmVudCB2IG9yIGsgdW50aWxsIGFsbCBwb3NzaWJsZSBrLTEgb3Igdi0xIHNldHMgaGF2ZSBiZWVuIGV2YWx1YXRlZCkgdG8gZ2V0IGEgc2Vuc2Ugb2YgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbC4gVGhpcyBpcyByZWFsbHkgdXNlZnVsIGZvciBmaW5lIHR1bmluZyBzcGVjaWZpYyBhc3BlY3RzIG9mIHRoZSBtb2RlbCBpbiBhIHByb2Nlc3MgY2FsbGVkIG1vZGVsIHR1bmluZy4KCgpIZXJlIGlzIGEgdmlzdWFsaXphdGlvbiBvZiBob3cgdGhlIGZvbGRzIGFyZSBjcmVhdGVkOgoKYGBge3IsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iODAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCJ2Zm9sZC5wbmciKSkKYGBgCgoKTm90ZTogUGVvcGxlIHR5cGljYWxseSBpZ25vcmUgc3BhdGlhbCBkZXBlbmRlbmNlIHdpdGggY3Jvc3MgdmFsaWRhdGlvbiBvZiBhaXIgcG9sbHV0aW9uIG1vbml0b3JpbmcgZGF0YSBpbiB0aGUgYWlyIHBvbGx1dGlvbiBmaWVsZCwgc28gd2Ugd2lsbCBkbyB0aGUgc2FtZS4gIEhvd2V2ZXIsIGl0IG1pZ2h0IG1ha2Ugc2Vuc2UgdG8gbGVhdmUgb3V0IGJsb2NrcyBvZiBtb25pdG9ycyByYXRoZXIgdGhhbiAgcmFuZG9tIGluZGl2aWR1YWwgbW9uaXRvcnMgdG8gaGVscCBhY2NvdW50IGZvciBzb21lIHNwYXRpYWwgZGVwZW5kZW5jZS4KClRoZSBbYHZmb2xkX2N2KClgXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JzYW1wbGUvcmVmZXJlbmNlL3Zmb2xkX2N2Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gZnVuY3Rpb24gb2YgdGhlIGByc2FtcGxlYCBwYWNrYWdlIGNhbiBiZSB1c2VkIHRvIHBhcnNlIHRoZSB0cmFpbmluZyBkYXRhIGludG8gZm9sZHMgZm9yIGstZm9sZC92LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbi4KClRoZSBgdmAgYXJndW1lbnQgc3BlY2lmaWVzIHRoZSBudW1iZXIgb2YgZm9sZHMgdG8gY3JlYXRlLgpUaGUgYHJlcGVhdHNgIGFyZ3VtZW50IHNwZWNpZmllcyBpZiBhbnkgc2FtcGxlcyBzaG91bGQgYmUgcmVwZWF0ZWQgYWNyb3NzIGZvbGRzIC0gZGVmdWFsdCBpcyBgRkFMU0VgClRoZSBgc3RyYXRhYCBhcmd1bWVudCBzcGVjaWZpZXMgYSB2YXJpYWJsZSB0byBzdHJhdGlmeSBzYW1wbGVzIGFjcm9zcyBmb2xkcyAoanVzdCBsaWtlIGluIGBpbml0aWFsX3NwbGl0KClgKS4KCkFnYWluIGJlY2F1c2UgdGhlc2UgYXJlIGNyZWF0ZWQgYXQgcmFuZG9tLCB3ZSBuZWVkIHRvIHVzZSB0aGUgYmFzZSBgc2V0LnNlZWQoKWAgZnVuY3Rpb24gaW4gb3JkZXIgdG8gb2J0YWluIHRoZSBzYW1lIHJlc3VsdHMgZWFjaCB0aW1lIHdlIGtuaXQgdGhpcyBkb2N1bWVudC4gR2VuZXJhbGx5IHNwZWFraW5nIHVzaW5nIDEwIGZvbGRzIGlzIGdvb2QgcHJhY3RpY2UsIGJ1dCB0aGlzIGRlcGVuZHMgb24gdGhlIHZhcmlhYmxpdHkgd2l0aGluIHlvdXIgZGF0YS4gV2UgYXJlIGdvaW5nIHRvIHVzZSA0IGZvciB0aGUgc2FrZSBvZiBleHBlZGllbmN5LiAKCmBgYHtyfQpzZXQuc2VlZCgxMjM0KQoKdmZvbGRfcG0gPC1yc2FtcGxlOjp2Zm9sZF9jdihkYXRhID0gdHJhaW5fcG0sIHYgPSA0KQoKdmZvbGRfcG0KCgp2Zm9sZF9wbSRzcGxpdHMkYDFgCnZmb2xkX3BtJHNwbGl0cyRgMmAKYGBgCgpPbmNlIHRoZSBmb2xkcyBhcmUgY3JlYXRlZCB0aGV5IGNhbiBiZSB1c2VkIHRvIGV2YWx1YXRlIHBlZm9ybWFuY2UgYnkgZml0dGluZyB0aGUgbW9kZWwgdG8gZWFjaCBvZiB0aGUgcmVzYW1wbGVzIHRoYXQgd2UgY3JlYXRlZDoKCgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI4MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsImNyb3NzX3ZhbGlkYXRpb24ucG5nIikpCmBgYAoKCldlIGNhbiBmaXQgdGhlIG1vZGVsIHRvIG91ciBjcm9zcyB2YWxpZGF0aW9uIGZvbGRzIHVzaW5nIHRoZSBgZml0X3Jlc2FtcGxlcygpYCBmdW5jdGlvbiBvZiB0aGUgYHR1bmVgIHBhY2thZ2UsIGJ5IHNwZWNpZnlpbmcgb3VyIHdvcmtmbG93IG9iamVjdCBhbmQgdGhlIGNyb3NzIHZhbGlkYXRpb24gZm9sZCBvYmplY3Qgd2UganVzdCBjcmVhdGVkLiBTZWUgW2hlcmVdKGh0dHBzOi8vdGlkeW1vZGVscy5naXRodWIuaW8vdHVuZS9yZWZlcmVuY2UvZml0X3Jlc2FtcGxlcy5odG1sKSBmb3IgbW9yZSBpbmZvcm1hdGlvbi4KCmBgYHtyfQpjb250cm9sIDwtIGNvbnRyb2xfcmVzYW1wbGVzKHNhdmVfcHJlZCA9IFRSVUUpCnJlc2FtcGxlX2ZpdCA8LXR1bmU6OmZpdF9yZXNhbXBsZXMoUE1fd2Zsb3csIHZmb2xkX3BtKQoKYGBgCgpXZSBjYW4gbm93IHRha2UgYSBsb29rIGF0IHZhcmlvdXMgbWV0cmljcyBvZiBwZXJmb3JtYW5jZSBiYXNlZCBvbiB0aGUgZml0IG9mIG91ciBjcm9zcyB2YWxpZGF0aW9uICJyZXNhbXBsZXMiLiBUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgc2hvd19iZXN0KClgIGZ1bmN0aW9uIG9mIHRoZSBgdHVuZWAgcGFja2FnZS4KCmBgYHtyfQp0dW5lOjpzaG93X2Jlc3QocmVzYW1wbGVfZml0LCBtZXRyaWMgPSAicm1zZSIsICJyc3EiKQpgYGAKCgojIyMgVHVuaW5nCgpOb3cgbGV0J3MgdHJ5IHNvbWUgdHVuaW5nLgoKTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rIGF0IGhvdyB0aGUgYWlyIHBvbGx1dGlvbiBtb25pdG9yIHZhbHVlcyB2YXJ5IHdpdGggdGhlIGxvY2F0aW9uIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuCgpgYGB7cn0KCnRyYWluX3BtICU+JSAKICBkcGx5cjo6c2VsZWN0KHZhbHVlLCBsb24sIGxhdCkgJT4lIAogIHRpZHlyOjpwaXZvdF9sb25nZXIoY29scyA9IGMobG9uLCBsYXQpLCAKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInByZWRpY3RvciIsIHZhbHVlc190byA9ICJsb2NfdmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9jX3ZhbHVlLCB2YWx1ZSkpICsgCiAgZ2VvbV9wb2ludChhbHBoYSA9IC4yKSArIAogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsgCiAgI3NjYWxlX3lfbG9nMTAoKSArCiAgZmFjZXRfd3JhcCh+IHByZWRpY3Rvciwgc2NhbGVzID0gImZyZWVfeCIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGRvZXMgbm90IGFwcGVhciB0byBiZSBhIHNpbmdsZSBsaW5lYXIgcmVsYXRpb25zaGlwIGZvciBlaXRoZXIgb2YgdGhlc2UgcHJlZGljdG9ycy4gVGh1cyB3ZSBtaWdodCB3YW50IHRvIHRoaW5rIGFib3V0IHVzaW5nICBbc3BsaW5lc10oaHR0cHM6Ly93d3cubWF0aC51aC5lZHUvfmppbmdxaXUvbWF0aDQzNjQvc3BsaW5lLnBkZikgb3IgdGhpcyhodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vbnVtZXJpY2FsLWludGVycG9sYXRpb24tbmF0dXJhbC1jdWJpYy1zcGxpbmUtNTJjMTE1N2I5OGFjKSBvciBqdXN0IHRoaXMoaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby90dW5lL2FydGljbGVzL2dldHRpbmdfc3RhcnRlZC5odG1sKSBvciB0aGlzKGh0dHBzOi8vd3d3LnBzeWNoLm1jZ2lsbC5jYS9taXNjL2ZkYS9leC1iYXNpcy1iMS5odG1sKSB0byBtb2RlbCB0aGUgcmVsYXRpb25zaGlwIGluIG91ciB0cmFpbmluZyBkYXRhIG1vcmUgY2xvc2VseS4gRm9yIGV4YW1wbGUgZm9yIHRoZSBsYXRpdHVkZSBwbG90IChsZWZ0KSBpZiB3ZSBoYWQgMiBsaW5lcyBhbmQgb25lIGJyZWFrcG9pbnQgY2FsbGVkIGEga25vdCBhcm91bmQgNDAsIHdpdGggdGhlIGZpcnN0IGxpbmUgaGF2aW5nIGEgcG9zaXRpdmUgc2xvcGUgYW5kIHRoZSBzZWNvbmQgd2l0aCBhIG5lZ2F0aXZlIHNsb3BlIHRoaXMgd291bGQgZml0IHRoZSBkYXRhIG1vcmUgc2ltaWxhcmx5IHRvIHRoZSBibHVlIGxpbmUgc2hvd24gaW4gdGhlIGZpZ3VyZS4KCldlIGNhbiB0dW5lIGZvciB0aGUgbnVtYmVyIG9mIGtub3RzIGJ5IHVzaW5nIGEgc3RlcCBmdW5jdGlvbiBpbiB0aGUgYHJlY2lwZXNgIHBhY2thZ2UgY2FsbGVkIGBzdGVwX25zKClgIHdoZXJlIG5zIHN0YW5kcyBmb3IgbmF0dXJhbCBzcGxpbmVzLiBJbiBvcmRlciB0byB0dW5lIGZvciB0aGUgbnVtYmVyIG9mIGtub3RzIG9yIGRlZ3JlZXMgb2YgZnJlZWRvbSwgd2UgY2FuIHNldCB0aGUgYGRlZ19mcmVlYCBhcmd1bWVudCB0byBgdHVuZSgpYC4gVGhpcyBpcyBoZWxwZnVsLCBiZWN1YXNlIHdlIGFyZW4ndCBleGFjdGx5IHN1cmUgaG93IGNsb3NlbHkgd2Ugc2hvdWxkIGJlIGZvbGxvd2luZyB0aGUgcmVsYXRpb25zaGlwIHdpdGggdGhlIHZhbHVlIGFuZCBvdXIgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBkYXRhIGluIG91ciB0cmFpbmluZyBkYXRhIHRvIGFjaGlldmUgZ29vZCBhY2N1cmFjeSB5ZXQga2VlcCBvdXIgbW9kZWwgZ2VuZXJhbGl6YWJsZSBmb3Igb3RoZXIgZGF0YS4gCgpUaGlzIGlzIHdoZW4gb3VyIGNyb3NzIHZhbGlkYXRpb24gbWV0aG9kcyBiZWNvbWUgcmVhbGx5IGhhbmR5LiBXZSBjYW4gdGVzdCBvdXQgZGlmZmVyZW50IHZhbHVlcyBmb3IgdGhlIGBkZWdfZnJlZWAgYXJndW1lbnQgYW5kIHNlZSBob3cgb3VyIG1vZGVsIHBlcmZvcm1hbmNlIHZhcmllcyBhY3Jvc3Mgb3VyIHRyYWluaW5nIGZvbGRzIHRvIHRyeSB0byBmaW5kIHRoZSBvcHRpbWFsIHZhbHVlLgoKV2Ugd2lsbCB1cGRhdGUgb3VyIHJlY2lwZSB0byBhZGQgdGhlc2Ugc3RlcHMuIEl0IGlzIGEgZ29vZCBpZGVhIHRvIGRvIHRoaXMgZm9yIGluZGl2aWR1YWwgcHJlZGljdG9ycyBiZWNhdXNlIHlvdSBjYW4gbmFtZSBlYWNoIHdpdGggdGhlIGB0dW5lYCBhcmd1bWVudCBzbyB0aGF0IHlvdSBjYW4ga2VlcCB0cmFjayBvZiBpdCBsYXRlci4gV2UgY2FuIHNlZSB3aGF0IHdlIGludGVuZCB0byB0dW5lIHdpdGggdGhlIGBwYXJhbWV0ZXJzKClgIGZ1bmN0aW9uIG9mIHRoZSBgZGlhbHNgIHBhY2thZ2UuIAoKU2VlIFtoZXJlXSh0aGlzKGh0dHBzOi8vdGlkeW1vZGVscy5naXRodWIuaW8vdHVuZS9hcnRpY2xlcy9nZXR0aW5nX3N0YXJ0ZWQuaHRtbCkpIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IGltcGxlbWVudGluZyB0aGlzIGluIHRpZHltb2RlbHMuCgpgYGB7cn0KCm5vdmVsX3JlYyAlPD4lCiAgc3RlcF9ucyhsb24sIGRlZ19mcmVlID0gdHVuZSgibG9uIGRmIikpICU+JQogIHN0ZXBfbnMobGF0LCAgZGVnX2ZyZWUgPSB0dW5lKCJsYXQgZGYiKSkKIyBub3ZlbF9yZWMgJTw+JQojICAgc3RlcF9ucyhsYXQsICBkZWdfZnJlZSA9IHR1bmUoKSkKIyBub3ZlbF9yZWMKCnBtX3BhcmFtIDwtZGlhbHM6OnBhcmFtZXRlcnMobm92ZWxfcmVjKQpwbV9wYXJhbQpgYGAKCkdlbmVhcmxseSB5b3UgY291bGQgdXNlIHRoZSBgZ3JpZF8qKClgIGZ1bmN0aW9ucyBvZiB0aGUgYGRpYWxzYCBwYWNrYWdlIHRvIGNyZWF0ZSB0aGUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBkZWdyZWVzIG9mIGZyZWVkb20gdG8gdGVzdCBmb3IgYm90aCB2YXJpYWJsZXMgdG8gb3B0aW1pemUgdGhlIG1vZGVsLiBJbiBvdXIgY2FzZSB3ZSBjYW4gdmlzYWJseSBzZWUgdGhhdCBpZiB3ZSBhZGQgbW9yZSB0aGFuIHNheSA0IG9yIDUgZGVncmVlcyBvZiBmcmVlZG9tIHdlIHdpbGwgbGlrZWx5IG92ZXJmaXQgdGhlIGRhdGEuIFNvIGluc3RlYWQgb2YgdXNpbmcgdGhlc2UgZnVuY3Rpb25zIHdlIHdpbGwgY3JlYXRlIG91ciBvd24gZ3JpZCB1c2luZyB0aGUgYmFzZSBgc2VxKClgIGFuZCBgZXhwYW5kLmdyaWQoKWAgZnVuY3Rpb25zLgoKYGBge3J9CiNhbiBleGFtcGxlIG9mIHdoYXQgeW91IGNvdWxkIGRvOgojc3BsaW5lX2dyaWQgPC1kaWFsczo6Z3JpZF9yZWd1bGFyKHBtX3BhcmFtLCBsZXZlbHMgPSAzKQpkZl92YWxzIDwtIHNlcSgxLCA1LCBieSA9IDIpCnNwbGluZV9ncmlkIDwtIGV4cGFuZC5ncmlkKGBsb24gZGZgID0gZGZfdmFscywgYGxhdCBkZmAgPSBkZl92YWxzKQpzcGxpbmVfZ3JpZApgYGAKCgpOb3cgd2Ugd2lsbCB0dW5lIHRoaXMgaHlwZXItcGFyYW1ldGVyIChkZWdyZWVzIG9mIGZyZWVkb20pIGZvciBib3RoIHRoZSBgbGF0YCBhbmQgYGxvbmAgdmFyaWFibGVzIHVzaW5nIG91ciBjcm9zcyB2YWxpZGF0aW9uIGZvbGRzLiBUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgdHVuZV9ncmlkKClgIGZ1bmN0aW9uIG9mIHRoZSBgdHVuZWAgcGFja2FnZS4KCmBgYHtyfQoKZGZfdHVuaW5nIDwtbG1fUE1fbW9kZWwgJT4lIAogIHR1bmU6OnR1bmVfZ3JpZChub3ZlbF9yZWMsIHJlc2FtcGxlcyA9dmZvbGRfcG0sIAogICAgICAgICAgICAgICAgICBncmlkID0gc3BsaW5lX2dyaWQpCgojZGZfdHVuaW5nIDwtUE1fd2Zsb3cgJT4lIHR1bmU6OnR1bmVfZ3JpZChyZXNhbXBsZXMgPXZmb2xkX3BtLCAKICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JpZCA9IHNwbGluZV9ncmlkLCAKICAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtX3NldCA9cG1fcGFyYW0pCgpkZl90dW5pbmcKYGBgCgoKCmBgYHtyfQoKZGZfdHVuaW5nICU+JQogIGNvbGxlY3RfbWV0cmljcygpCgpzaG93X2Jlc3QoZGZfdHVuaW5nLCBtZXRyaWMgPSAicm1zZSIsIG4gPTEpCmBgYAoKCgoKIyMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIHdpdGggUENBCgpXZSBjYW4gY3JlYXRlIGFub3RoZXIgd29ya2Zsb3cgdG8gc2VlIGhvdyBtb2RlbCBwZWZyb21hbmNlIGNvbXBhcmVzIHVzaW5nIGEgZGlmZmVyZW50IG1vZGVsLiBJbiB0aGlzIGNhc2Ugd2UgYXJlIGdvaW5nIHRvIHBlcmZvcm0gc29tZXRoaW5nIGNhbGxlZCBbUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyBvciBQQ0FdKGh0dHBzOi8vbWVkaXVtLmNvbS9Ac2F2YXN0YW1pcmtvL3BjYS1hLWxpbmVhci10cmFuc2Zvcm1hdGlvbi1mOGFhY2Q0ZWIwMDcpe3RhcmdldD0iX2JsYW5rIn0uIAoKU28gd2hhdCBpcyBQQ0E/CgpQQ0EgaXMgYSB3aWRlbHkgdXNlZCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gbWV0aG9kIChhIGZvcm0gb2YgdW5zcGVydmlzZWQgbWFjaGluZSBsZWFybmluZykuIEl0IGNyZWF0ZXMgbmV3IHZhcmlhYmxlcyB0aGF0IGNhcHR1cmUgdGhlIG1vc3QgdmFyaWF0aW9uIHdpdGhpbiB0aGUgZGF0YSwgeWV0IHJlZHVjZSB0aGUgZGF0YSBkb3duIHRvIGp1c3QgYSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMuIEl0IGRvZXMgc28gYnkgdHJhbnNmb3JpbWluZyB0aGUgZGF0YSB1c2luZyBbb3J0aG9nb25hbF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT3J0aG9nb25hbGl0eSM6fjp0ZXh0PUluJTIwbWF0aGVtYXRpY3MlMkMlMjBvcnRob2dvbmFsaXR5JTIwaXMlMjB0aGUsdSUyQyUyMHYpJTIwJTNEJTIwMC4pe3RhcmdldD0iX2JsYW5rIn0gbGluZWFyIHRyYW5zZm9ybWF0aW9uLiBJbiBvdGhlcndvcmRzLCBpdCBjcmVhdGVzIG5ldyB2YXJpYWJsZXMgdGhhdCBhcmUgW2xpbmVhciBjb21iaW5hdGlvbnNdKGh0dHBzOi8vd3d3Lm1hdGhib290Y2FtcHMuY29tL2xpbmVhci1jb21iaW5hdGlvbnMtdmVjdG9ycy8pe3RhcmdldD0iX2JsYW5rIn0gb2YgdGhlIHZhcmlhYmxlcyB3aXRoaW4gdGhlIGRhdGEuIEltcG9ydGFudGx5IHRoZXNlIG5ldyB2YXJpYWJsZXMgYXJlIG9ydGhvZ29uYWwsIG1lYW5pbmcgdGhhdCB0aGUgbmV3IHZhcmlhYmxlcyBoYXZlIHplcm8gY292YXJpYW5jZS4gSW4gc2ltcGxlciB0ZXJtcywgd2UgYXJlIGV4cGVzc2luZyB1bmlxdWUgdHlwZXMgb2YgdmFyaWF0aW9uIHdpdGhpbiB0aGUgZGF0YSBhcyBuZXcgdmFyaWFibGVzLgoKQ2hlY2sgb3V0IHRoaXMgW3ZpZGVvXShodHRwczovL3lvdXR1LmJlL19VVkhuZUJVQlcwKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciBtb3JlIGluZm9ybWF0aW9uLgoKYGBge3J9CmxtX1BDQV9yZWMgPC1yZWNpcGUodHJhaW5fcG0pICU+JQogICAgdXBkYXRlX3JvbGUoZXZlcnl0aGluZygpLCBuZXdfcm9sZSA9ICJwcmVkaWN0b3IiKSU+JQogICAgdXBkYXRlX3JvbGUodmFsdWUsIG5ld19yb2xlID0gIm91dGNvbWUiKSU+JQogICAgdXBkYXRlX3JvbGUoaWQsIG5ld19yb2xlID0gImlkIHZhcmlhYmxlIikgJT4lCiAgICB1cGRhdGVfcm9sZSgiZmlwcyIsIG5ld19yb2xlID0gImNvdW50eSBpZCIpICU+JQogICAgc3RlcF9kdW1teShzdGF0ZSwgY291bnR5LCBjaXR5LCB6Y3RhKSAlPiUKICAgIHN0ZXBfcGNhKGFsbF9wcmVkaWN0b3JzKCkpIApgYGAKCkxldCdzIHRha2UgYSBsb29rIHRvIHNlZSB3aGF0IHRoZSBgc3RlcF9wY2FgIGZ1bmN0aW9uIGRvZXMgdG8gb3VyIHByZWRpY3RvcnMuIFRvIGRvIHNvIHJlY2FsbCB0aGF0IHdlIG5lZWQgdG8gdXNlIHRoZSBgcHJlcGAgYW5kIGBqdWljZWAgIGZ1bmN0aW9ucyBvZiB0aGUgYHJlY2lwZXNgIHBhY2thZ2UgIG9uIG91ciByZWNpcGUuCgpgYGB7cn0KcHJlcHBlZF9yZWMgPC0gcHJlcChsbV9QQ0FfcmVjLCB2ZXJib3NlID0gVFJVRSwgcmV0YWluID0gVFJVRSApCmp1aWNlZF90cmFpbjwtIGp1aWNlKHByZXBwZWRfcmVjKQpnbGltcHNlKGp1aWNlZF90cmFpbikKYGBgCgpXZSBzdGlsbCB3YW50IHRvIHVzZSB0aGUgYGxtYCBwYWNrYWdlIGZvciBvdXIgcmVncmVzc2lvbiBzbyB3ZSBjYW4gdXNlIHRoZSBzYW1lIG1vZGVsIG9iamVjdCBhcyBiZWZvcmU6CmBgYHtyfQpsbV9QTV9tb2RlbApgYGAKCgoKYGBge3J9CnBjYV93ZmxvdyA8LXdvcmtmbG93czo6d29ya2Zsb3coKSAlPiUKICAgICAgICAgICAgd29ya2Zsb3dzOjphZGRfcmVjaXBlKGxtX1BDQV9yZWMpICU+JQogICAgICAgICAgICB3b3JrZmxvd3M6OmFkZF9tb2RlbChsbV9QTV9tb2RlbCkKCgpwY2Ffd2Zsb3cKYGBgCgoKCgoKUmVtZW1iZXIgdGhhdCB1c2luZyBgd29ya2Zsb3dzYCB3ZSBkb250IGFjdHVhbGx5IG5lZWQgdG8gcHJlcCBvdXIgcmVjaXBlLCB3ZSBjYW4ganVzdCBmaXQgb3VyIG1vZGVsIGRpcmVjdGx5LiAKCkZpdCB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBzYW1wbGVzOgoKYGBge3J9CnJlc2FtcGxlX3BjYV9maXQgPC10dW5lOjpmaXRfcmVzYW1wbGVzKHBjYV93ZmxvdywgdmZvbGRfcG0pCmBgYAoKCkxvb2sgYXQgdGhlIHBlcmZvcm1hbmNlOgpgYGB7cn0KY29sbGVjdF9tZXRyaWNzKHJlc2FtcGxlX3BjYV9maXQpCmBgYAoKQW5kIHdlIGNhbiBjb21wYXJlIHRoaXMgd2l0aCBvdXIgcHJldmlvdXMgcGVyZm9ybWFuY2U6CgpgYGB7cn0KY29sbGVjdF9tZXRyaWNzKHJlc2FtcGxlX2ZpdCkKYGBgCgpTbyB3ZSBjYW4gc2VlIHRoYXQgb3VyIHBlcmZvcm1hbmNlIGlzbid0IHF1aXRlIGFzIGdvb2QgLSBlc3BlY2lhbGx5IGlmIHdlIGxvb2sgYXQgdGhlIGBybXNlYCB2YWx1ZS4gIAoKCiMjIyBSYW5kb20gRm9yZXN0CgoKTm93IGZvciBvbmUgbGFzdCByZWNpcGUsIHdlIGFyZSBnb2luZyB0byBwcmVkaWN0IHVzaW5nIGEgZGVjaXNpb24gdHJlZSBtZXRob2QgY2FsbGVkIHJhbmRvbSBmb3Jlc3RzLgoKQSBkZWNpc2lvbiB0cmVlIGlzIGEgdG9vbCB0byBwYXJ0aXRpb24gZGF0YSBiYXNlZCBvbiBhIHNlcmllcyBvZiBzZXF1ZW50aWFsIGJpbmFyeSBkZWNpc2lvbnMuCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vbWlyby5tZWRpdW0uY29tL21heC8xMDAwLzEqTE1vSm1YQ3NRbGNpR1RFeW9TTjM5Zy5qcGVnIikKCmBgYAoKIyMjIyBbW3NvdXJjZV0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3VuZGVyc3RhbmRpbmctcmFuZG9tLWZvcmVzdC01ODM4MWUwNjAyZDIpXQoKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9taXJvLm1lZGl1bS5jb20vbWF4LzE0MDAvMCpmX3FRUEZwZG9mV0dMUXFjLnBuZyIpCmBgYAoKCkluIG91ciBjYXNlLCB0aGUgcmFuZG9tIGZvcmVzdCBhbGdvcml0aG0gdGhhdCB3ZSBhcmUgd29ya2luZyB3aXRoIGRvZXMgbm90IHdvcmsgd2VsbCB3aGVuIHRoZXJlIGFyZSBjYXRlZ29yaWNhbCB2YXJpYWJlbHMgd2l0aCBtb3JlIHRoYW4gNTMgbGV2ZWxzLCBzbyB3ZSB3aWxsIG5lZWQgdG8gcmVtb3ZlIHRoZSBgemN0YWAgdmFyaWFibGUuCgpgYGB7cn0KClJGX3JlYyA8LXJlY2lwZSh0cmFpbl9wbSkgJT4lCiAgICB1cGRhdGVfcm9sZShldmVyeXRoaW5nKCksIG5ld19yb2xlID0gInByZWRpY3RvciIpJT4lCiAgICB1cGRhdGVfcm9sZSh2YWx1ZSwgbmV3X3JvbGUgPSAib3V0Y29tZSIpJT4lCiAgICB1cGRhdGVfcm9sZShpZCwgbmV3X3JvbGUgPSAiaWQgdmFyaWFibGUiKSAlPiUKICAgIHVwZGF0ZV9yb2xlKCJmaXBzIiwgbmV3X3JvbGUgPSAiY291bnR5IGlkIikgJT4lCiAgICBzdGVwX3N0cmluZzJmYWN0b3IoInN0YXRlIiwgImNvdW50eSIsICJjaXR5IikgJT4lCiAgICBzdGVwX3JtKCJjb3VudHkiKSAlPiUKICAgIHN0ZXBfcm0oImlkIikgJT4lCiAgICBzdGVwX3JtKCJmaXBzIiklPiUKICAgIHN0ZXBfcm0oInpjdGEiKSAlPiUKICAgIHN0ZXBfY29ycihhbGxfbnVtZXJpYygpKSU+JQogICAgc3RlcF9uenYoYWxsX251bWVyaWMoKSkKYGBgCgoKYGBge3J9CgpQTXRyZWVfbW9kZWwgPC0gcGFyc25pcDo6cmFuZF9mb3Jlc3QobXRyeSA9IDEwLCBtaW5fbiA9IDMsIG1vZGUgPSAicmVncmVzc2lvbiIpClBNdHJlZV9tb2RlbAoKUkZfUE1fbW9kZWwgPC0gCiAgUE10cmVlX21vZGVsICAlPiUKICBwYXJzbmlwOjpzZXRfZW5naW5lKCJyYW5kb21Gb3Jlc3QiKSAlPiUKICBzZXRfbW9kZSgicmVncmVzc2lvbiIpCgpSRl9QTV9tb2RlbAoKClJGX3dmbG93IDwtd29ya2Zsb3dzOjp3b3JrZmxvdygpICU+JQogICAgICAgICAgICB3b3JrZmxvd3M6OmFkZF9yZWNpcGUoUkZfcmVjKSAlPiUKICAgICAgICAgICAgd29ya2Zsb3dzOjphZGRfbW9kZWwoUkZfUE1fbW9kZWwpClJGX3dmbG93CmBgYAoKRml0dGluZyB0aGUgZGF0YSB3aXRoIGp1c3QgcGFyc25pcCBhbmQgd2l0aCB0aGUgd29ya2Zsb3c6CmBgYHtyfQoKIyNqdXN0IHBhcnNuaXAKUkZfZml0IDwtIFJGX1BNX21vZGVsICU+JSAKcGFyc25pcDo6Zml0KHZhbHVlIH4uLCBkYXRhID1qdWljZWRfdHJhaW5fcmVhZHkpCgojIyB3aXRoIHdvcmtmbG93ClJGX3dmbG93X2ZpdCA8LSBwYXJzbmlwOjpmaXQoUkZfd2Zsb3csIGRhdGEgPSB0cmFpbl9wbSkKUkZfd2Zsb3dfZml0CmBgYAoKCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSB0b3AgMTAgY29udHJpYnV0aW5nIHZhcmlhYmxlczoKCmBgYHtyfQpSRl93Zmxvd19maXQlPiUgCiAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUgCiAgdmlwKG51bV9mZWF0dXJlcyA9IDEwKQpgYGAKCkludGVyZXN0aW5nLCBpbiB0aGUgcHJldmlvdXMgbW9kZWwgdGhlIENNQVEgdmFsdWVzIHdlcmUgYWxzbyBpbXBvcnRhbnQsIGhvd2V2ZXIgdGhlIHZhcmlhYmxlIGFib3V0IGlmIHRoZSBtb25pdG9yIHdhcyBsb2NhdGVkIGluIENhbGlmb3JuaWEgb3Igbm90IHdhcyBhbHNvIHZlcnkgcHJlZGljdGl2ZS4gCgpOb3cgbGV0J3MgdGFrZSBhIGxvb2sgYXQgbW9kZWwgcGVyZm9ybWFuY2UgYnkgZml0dGluZyB0aGUgZGF0YSB1c2luZyBjcm9zcyB2YWxpZGF0aW9uOgpgYGB7cn0KcmVzYW1wbGVfUkZfZml0IDwtdHVuZTo6Zml0X3Jlc2FtcGxlcyhSRl93ZmxvdywgdmZvbGRfcG0pCmNvbGxlY3RfbWV0cmljcyhyZXNhbXBsZV9SRl9maXQpCmBgYAoKTm93IGxldCdzIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoaXMgbW9kZWwgd2l0aCB0aGUgb3RoZXJzOgoKYGBge3J9CiMgb3VyIGluaXRpYWwgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWw6CmNvbGxlY3RfbWV0cmljcyhyZXNhbXBsZV9maXQpCiMgb3VyIGluaXRpYWwgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBsYXQvbG9uIGRlZ3JlZXMgb2YgZnJlZWRvbSB0dW5pbmc6CnNob3dfYmVzdChkZl90dW5pbmcsIG1ldHJpYyA9ICJybXNlIiwgbiA9MSkKIyBvdXIgUENBIG1vZGVsOgpjb2xsZWN0X21ldHJpY3MocmVzYW1wbGVfcGNhX2ZpdCkKCmBgYApPaywgc28gb3VyIGZpcnN0IG1vZGVsIGhhZCBhIG1lYW4gcm1zZSB2YWx1ZSBvZiAyLjE4LgpUaGUgbW9kZWwgd2l0aCB0aGUgbGF0L2xvbmcgZGVncmVlcyBvZiBmcmVlZG9tICB0dW5pbmcgaGFkIGEgbWVhbiBybXNlIHZhbHVlIG9mIDIuMDIsIHRodXMgc2hvd2luZyBzb21lIGltcHJvdmVtZW50LgpUaGUgUENBIG1vZGVsIGhhZCBhIG1lYW4gcm1zZSB2YWx1ZSBvZiAyLjc2LgoKSXQgbG9va3MgbGlrZSB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbCBoYWQgdGhlIGxvd2VzdCBybXNlIHZhbHVlIG9mIDEuNzkuCgoKSWYgd2UgdHVuZWQgb3VyIHJhbmRvbSBmb3Jlc3QgbW9kZWwgYmFzZWQgb24gdGhlIG51bWJlciBvZiB0cmVlcyBvciB0aGUgdmFsdWUgZm9yIGBtdHJ5YCAod2hpY2ggaXMgIlRoZSBudW1iZXIgb2YgcHJlZGljdG9ycyB0aGF0IHdpbGwgYmUgcmFuZG9tbHkgc2FtcGxlZCBhdCBlYWNoIHNwbGl0IHdoZW4gY3JlYXRpbmcgdGhlIHRyZWUgbW9kZWxzIiksIHdlIG1pZ2h0IGdldCBhIG1vZGVsIHdpdGggZXZlbiBiZXR0ZXIgcGVyZm9ybWFuY2UuCgoKSG93ZXZlciwgb3VyIGNyb3NzIHZhbGlkZWQgbWVhbiBybXNlIHZhbHVlIG9mIDEuNzkgaXMgcXVpdGUgZ29vZCBiZWNhdXNlIG91ciByYW5nZSBvZiB0cnVlIG91dGNvbWUgdmFsdWVzIGlzIG11Y2ggbGFyZ2VyOmByIHJhbmdlKHRlc3RfcG0kdmFsdWUpYC4KCiMjIyBGaW5hbCBNb2RlbCBQZXJmb3JtYW5jZSBFdmFsdWF0aW9uCgoKTm93IHRoYXQgd2UgaGF2ZSBkZWNpZGVkIHRoYXQgd2UgaGF2ZSByZXNvbmFibGUgcGVyZm9ybWFuY2Ugd2l0aCB0aGUgdHJhaW5pbmcgZGF0YSwgd2UgY291bGQgc3RvcCBoZXJlIGFuZCB1c2UgdGhlIGB5YXJkc3RpY2tgIHBhY2thZ2UgKGFuZCBgdHVuZWAgaWYgdXNpbmcgYHdvcmtmbG93c2AgdG8gZml0IG91ciBtb2RlbCkgdG8gZXZhbHVhdGUgcGVyZm9ybWFuY2Ugd2l0aCBvdXIgdGVzdGluZyBkYXRhLiAKClNvIG5vdyB3ZSB3aWxsIHVzZSBvdXIgcmFuZG9tIGZvcmVzdHMgbW9kZWwgdG8gcHJlZGljdCB2YWx1ZXMgZm9yIHRoZSBtb25pdG9ycyBpbiB0aGUgdGVzdGluZyBkYXRhLgoKVXNpbmcgYHBhcnNuaXBgIHdlIHdvdWxkIG5lZWQgdG8gdXNlIHRoZSBiYWtlZCBkYXRhIHRlc3RpbmcgZGF0YS4gV2l0aCB0aGUgYHdvcmtmbG93c2AgcGFja2FnZSwgd2UgY291bGQgdXNlIHRoZSByYXcgdGVzdGluZyBkYXRhLgoKSW1wb3J0YW50bHksIElEIHZhcmlhYmxlcyBhcmUgbm90IGRlbHQgd2l0aCBhcyBuaWNlbHkgYXMgd2l0aCB0aGUgYHdvcmtmbG93c2AgcGFja2FnZSBzbyB3ZSB3b3VsZCBuZWVkIHRvIHJlbW92ZSB0aGVtLiBXZSBkaWQgdGhpcyBhYm92ZSB3aGVuIGNyZWF0ZWQgdGhlIHByb2Nlc3NlZCB0cmFpbmluZyBkYXRhIGZvciB0aGlzIG1vZGVsLCB0aGUgYGp1aWNlZF90cmFpbmAgZGF0YSBhcyB3ZWxsLgoKYGBge3J9CgogYmFrZWRfdGVzdF9wbV9yZWFkeSA8LWJha2VkX3Rlc3RfcG0gJT4lc2VsZWN0KCAtImlkIiwgLSJmaXBzIikKIHZhbHVlc19wcmVkX3BhcnNuaXAgPC1wcmVkaWN0KFJGX2ZpdCwgYmFrZWRfdGVzdF9wbV9yZWFkeSkKIHZhbHVlc19wcmVkX3BhcnNuaXAKIAojIHVzaW5nIHRoZSB3b3JrZmxvd3MgdmVyc2lvbgp2YWx1ZXNfcHJlZF93ZnMgPC0gCiAgcHJlZGljdChSRl93Zmxvd19maXQsIHRlc3RfcG0pICU+JSAKICBiaW5kX2NvbHModGVzdF9wbSAlPiUgc2VsZWN0KHZhbHVlLCBmaXBzLCBjb3VudHksIGlkKSkgCnZhbHVlc19wcmVkX3dmcwoKI21vZGVsIHBlcmZvcm1hbmNlIHdpdGggdGVzdCBzZXQKeWFyZHN0aWNrOjptZXRyaWNzKHZhbHVlc19wcmVkX3dmcywgdHJ1dGggPSB2YWx1ZSwgZXN0aW1hdGUgPSAucHJlZCkKCmBgYApBd2Vzb21lISBXZSBjYW4gc2VlIHRoYXQgb3VyIHJtc2Ugb2YgMS40OSBpcyBxdWl0ZSBzaW1pbGFyIHdpdGggb3VyIHRlc3RpbmcgZGF0YS4gV2UgYWNoaWV2ZWQgcXVpdGUgZ29vZCBwZXJmb3JtYW5jZSwgd2hpY2ggc3VnZ2VzdHMgdGhhdCB3ZSB3b3VsZCBjb3VsZCBwcmVkaWN0IG90aGVyIGxvY2F0aW9ucyB3aXRoIG1vcmUgc3BhcnNlIG1vbml0b3JpbmcgYmFzZWQgb24gb3VyIHByZWRpY3RvcnMgd2l0aCByZXNvbmFibGUgYWNjdXJhY3kuCgpXZSBjb3VsZCBhbHNvIHVzZSB0aGUgYGxhc3RfZml0KClgIGZ1bmN0aW9uIG9mIHRoZSBgdHVuZWAgcGFja2FnZSB0byBsb29rIGF0IHBlcmZvcm1hbmNlIGlmIHdlIGNob3NlIHRvIGNyZWF0ZSBhIHdvcmtmbG93IHVzaW5nIHRoZSBgd29ya2Zsb3dzYCBwYWNrYWdlLgoKYGBge3J9Cm92ZXJhbGxmaXQgPC10dW5lOjpsYXN0X2ZpdChSRl93ZmxvdywgcG1fc3BsaXQpCiAjIG9yCm92ZXJhbGxmaXQgPC1SRl93ZmxvdyAlPiUKICB0dW5lOjpsYXN0X2ZpdChwbV9zcGxpdCkKCm92ZXJhbGxmaXQkLm1ldHJpY3NbWzFdXQpgYGAKCldlIGNvdWxkIGNoZWNrIG91dCB0ZXN0IHBlcmZvcm1hbmNlIHVzaW5nIHRoZSBgY29sbGVjdF9tZXRyaWNzKClgIGZ1bmN0aW9uIG9mIHRoZSBgdHVuZWAgcGFja2FnZS4KCgpgYGB7cn0KdGVzdF9wZXJmb3JtYW5jZSA8LSBvdmVyYWxsZml0ICAlPiV0dW5lOjpjb2xsZWN0X21ldHJpY3MoKQp0ZXN0X3BlcmZvcm1hbmNlCmBgYAoKSGVyZSB5b3UgY2FuIHNlZSB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSB0ZXN0IHNldCAodGhlIDI5MiByb3dzIHdpdGggcHJlZGljaXRpb25zIG91dCBvZiB0aGUgODc2IG9yaWdpbmFsIG1vbml0b3IgdmFsdWVzKSBhbHNvIHVzaW5nIHRoZSBgdHVuZWAgcGFja2FnZSB3aXRoIHRoZSBgY29sbGVjdF9wcmVkaWN0aW9ucygpYCBmdW5jdGlvbi4KCiMjIyMgey5zY3JvbGxhYmxlIH0KYGBge3J9CnRlc3RfcHJlZGljdGlvbnMgPC0gb3ZlcmFsbGZpdCAgJT4ldHVuZTo6Y29sbGVjdF9wcmVkaWN0aW9ucygpCnRlc3RfcHJlZGljdGlvbnMKYGBgCgoKIyMgRGF0YSBWaXN1YWxpemF0aW9uCgpPdXIgbWFpbiBxdWVzdGlvbiBmb3IgdGhpcyBjYXNlIHN0dWR5IHdhczogIAoKMSkgQ2FuIHdlIHByZWRpY3QgYW5udWFsIGF2ZXJhZ2UgYWlyIHBvbGx1dGlvbiBjb25jZW50cmF0aW9ucyBhdCB0aGUgZ3JhbnVsYXJpdHkgb2YgemlwIGNvZGUgcmVnaW9uYWwgbGV2ZWxzIHVzaW5nIHByZWRpY3RvcnMgc3VjaCBhcyBkYXRhIGFib3V0IHBvcHVsYXRpb24gZGVuc2l0eSwgdXJiYW5pemF0aW9uLCByb2FkIGRlbnNpdHksIGFzIHdlbGwgYXMsIHNhdGVsbGl0ZSBwb2xsdXRpb24gZGF0YSBhbmQgY2hlbWljYWwgbW9kZWxpbmcgZGF0YT8KCldlIGhhdmUgaW5kZWVkIGNyZWF0ZWQgYSBtb2RlbCB0aGF0IGNhbiBwcmVkaWN0IGZpbmUgcGFydGljdWxhdGUgbWF0dGVyIGFpciBwb2xsdXRpb24gbGV2ZWxzIGJhc2VkIG9uIG91ciBwcmVkaWN0b3IgdmFyaWFibGVzLgoKTm93IGxldCdzIG1ha2UgYSBwbG90IG9mIG91ciBwcmVkaWN0ZWQgdmFsdWVzIGFuZCB0aGUgdHJ1ZSB2YWx1ZXMuCgpGaXJzdCwgbGV0J3Mgc3RhcnQgYnkgbWFraW5nIGEgcGxvdCBvZiBvdXIgbW9uaXRvcnM6CgpXZSB3aWxsIHVzZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzIHRvIGNyZWF0ZSBhIG1hcCBvZiB0aGUgVVM6CjEpYHNmYAoyKWBybmF0dXJhbGVhcnRoYAozKWBybmF0dXJhbGVhcnRoZGF0YWAKNClgcmdlb3NgCgpBY2NvcmRpbmcgdG8gdGhpcyBbbGluayBvbiB3aWtpcGVkaWFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfZXh0cmVtZV9wb2ludHNfb2ZfdGhlX1VuaXRlZF9TdGF0ZXMjV2VzdGVybm1vc3QpLCB0aGVzZSBhcmUgdGhlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgYm91bmRzIG9mIHRoZSBjb250aW50ZW50YWwgVVMuCgp0b3AgPSA0OS4zNDU3ODY4ICMgbm9ydGggbGF0CmxlZnQgPSAtMTI0Ljc4NDQwNzkgIyB3ZXN0IGxvbmcKcmlnaHQgPSAtNjYuOTUxMzgxMiAjIGVhc3QgbG9uZwpib3R0b20gPSAgMjQuNzQzMzE5NSAjIHNvdXRoIGxhdAoKV2Ugd2lsbCBzdGFydCB3aXRoIGdldHRpbmcgYW4gb3V0bGluZSBvZiB0aGUgVVMgd2l0aCB0aGUgYG5lX2NvdW50cmllcygpYCBmdW5jdGlvbiBvZiB0aGUgYHJuYXR1cmFsZWFydGhgIHBhY2thZ2UuCmBgYHtyfQoKbGlicmFyeSgic2YiKQpsaWJyYXJ5KCJybmF0dXJhbGVhcnRoIikKI2xpYnJhcnkoInJuYXR1cmFsZWFydGhkYXRhIikKbGlicmFyeSgicmdlb3MiKQoKd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikKY2xhc3Mod29ybGQpCgpnZ3Bsb3QoZGF0YSA9IHdvcmxkKSArCiAgICBnZW9tX3NmKCkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gcG0sIGFlcyh4ID0gbG9uLCB5ID0gbGF0KSwgc2l6ZSA9IDIsIAogICAgICAgIHNoYXBlID0gMjMsIGZpbGwgPSAiZGFya3JlZCIpICsKICAgIGNvb3JkX3NmKHhsaW0gPSBjKC0xMjUsIC02NiksIHlsaW0gPSBjKDI0LjUsIDUwKSwgZXhwYW5kID0gRkFMU0UpCmBgYAoKTm93IGxldCdzIGFkZCBjb3VudHkgbGluZXMuCgpDb3VudHkgZGF0YSBpcyBhdmFpbGFibGUgZnJvbSB0aGUgYG1hcHNgIHBhY2thZ2UuCgpgYGB7cn0KCmNvdW50aWVzIDwtIHN0X2FzX3NmKG1hcHM6Om1hcCgiY291bnR5IiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpCgptb25pdG9ycyA8LWdncGxvdChkYXRhID0gd29ybGQpICsKICAgZ2VvbV9zZihkYXRhID0gY291bnRpZXMsIGZpbGwgPSBOQSwgY29sb3IgPSBncmF5KC41KSkrCiAgICBnZW9tX3BvaW50KGRhdGEgPSBwbSwgYWVzKHggPSBsb24sIHkgPSBsYXQpLCBzaXplID0gMiwgCiAgICAgICAgc2hhcGUgPSAyMywgZmlsbCA9ICJkYXJrcmVkIikgKwogICAgY29vcmRfc2YoeGxpbSA9IGMoLTEyNSwgLTY2KSwgeWxpbSA9IGMoMjQuNSwgNTApLCBleHBhbmQgPSBGQUxTRSkrCiAgZ2d0aXRsZSgiTW9uaXRvciBMb2NhdGlvbnMiKQoKbW9uaXRvcnMKYGBgCgpOb3cgbGV0J3MgYWRkIGEgZmlsbCBhdCB0aGUgY291bnR5IGxldmVsIGZvciB0aGUgdHJ1ZSBtb25pdG9yIHZhbHVlcyBvZiBhaXIgcG9sbHV0aW9uOgoKYGBge3J9CmxpYnJhcnkobHdnZW9tKQpsaWJyYXJ5KHN0cmluZ3IpCgpoZWFkKGNvdW50aWVzKQoKcG0gPC1yZWFkcjo6cmVhZF9jc3YoaGVyZSgiZG9jcyIsICJwbTI1X2RhdGEuY3N2IikpCmNvdW50aWVzICU8PiUgdGlkeXI6OnNlcGFyYXRlKElELCBpbnRvID0gYygic3RhdGUiLCAiY291bnR5IiksIHNlcCA9ICIsIikKCmNvdW50aWVzW1siY291bnR5Il1dIDwtc3RyX3RvX3RpdGxlKGNvdW50aWVzW1siY291bnR5Il1dKQoKbWFwX2RhdGEgPC1pbm5lcl9qb2luKGNvdW50aWVzLCBwbSwgYnkgPSAiY291bnR5IikKCnRydXRoPC1nZ3Bsb3QoZGF0YSA9IHdvcmxkKSArCiAgICBnZW9tX3NmKCkgKwogICAgZ2VvbV9zZihkYXRhID0gbWFwX2RhdGEsIGFlcyhmaWxsID0gdmFsdWUpKSArCiAgICAjc2NhbGVfZmlsbF92aXJpZGlzX2ModHJhbnMgPSAic3FydCIsIGFscGhhID0gLjQpICsKICAgICAgI2dlb21fcG9pbnQoZGF0YSA9IHBtLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCksIHNpemUgPSAxLjUsIGFscGhhID0gMiwKICAgICAgICMgc2hhcGUgPSAyMywgZmlsbCA9ICJkYXJrcmVkIikgKwogICAgICAgIGNvb3JkX3NmKHhsaW0gPSBjKC0xMjUsIC02NiksIHlsaW0gPSBjKDI0LjUsIDUwKSwgZXhwYW5kID0gRkFMU0UpKwogICAgICBnZ3RpdGxlKCJNb25pdG9yIFBNMi41IGxldmVscyIpCnRydXRoCgpgYGAKCgpOb3cgbGV0J3MgZG8gdGhlIHNhbWUgd2l0aCBvdXIgcHJlZGljdGVkIHZhbHVlcy4KCkxldCdzIGdyYWIgYm90aCB0aGUgdGVzdGluZyBhbmQgdHJhaW5pbmcgZml0dGVkIHZhbHVlcyBzbyB0aGF0IHdlIGhhdmUgYXMgbXVjaCBkYXRhIGFzIHBvc3NpYmxlLiAKSW4gdGhpcyBjYXNlLCB0aGUgb3V0cHV0IHN0cnVjdHVyZSBmb3IgdGhlIHRyYWluaW5nIGRhdGEgZml0IGlzIHNsaWdodGx5IGRpZmZlcmVudCB1c2luZyBgcmFuZG9tRm9yZXN0YC4gVGhlIGZpdHRlZCB2YWx1ZXMgYXJlIGNhbGxlZCBgcHJlZGljdGVkYCBhbmQgdGhlIGBicm9vbWAgZnVuY3Rpb25zIGxpa2UgYHRpZHkoKWAgYW5kIGBhdWdtZW50KClgIHdpbGwgbm90IHdvcmsuIFNvIHdlIHdpbGwgbWFudWFsbHkgZ3JhYiB0aGUgZml0dGVkIHRyYWluaW5nIGRhdGEgdmFsdWVzLgoKYGBge3J9CgojdGVzdCBkYXRhCnZhbHVlc19wcmVkX3dmcwoKI3RyYWluaW5nIGRhdGEKdHJhaW5pbmdfUkZfZXN0aW1hdGVzIDwtUkZfZml0W1siZml0Il1dW1sicHJlZGljdGVkIl1dCnRyYWluaW5nX1JGX2VzdGltYXRlcyA8LXRpYmJsZSgiLnByZWQiID10cmFpbmluZ19SRl9lc3RpbWF0ZXMsCnZhbHVlPSB0cmFpbl9wbSR2YWx1ZSwgZmlwcyA9IHRyYWluX3BtJGZpcHMsICBjb3VudHkgPSB0cmFpbl9wbSRjb3VudHksIGlkID10cmFpbl9wbSRpZCkKCmFsbF9wcmVkIDwtYmluZF9yb3dzKHZhbHVlc19wcmVkX3dmcywgdHJhaW5pbmdfUkZfZXN0aW1hdGVzICkKCgpgYGAKCgpgYGB7cn0KbWFwX2RhdGEgPC1pbm5lcl9qb2luKGNvdW50aWVzLCBhbGxfcHJlZCwgYnkgPSAiY291bnR5IikKCnByZWQgPC1nZ3Bsb3QoZGF0YSA9IHdvcmxkKSArCiAgICBnZW9tX3NmKCkgKwogICAgZ2VvbV9zZihkYXRhID0gbWFwX2RhdGEsIGFlcyhmaWxsID0gLnByZWQpKSArCiAgICAjc2NhbGVfZmlsbF92aXJpZGlzX2ModHJhbnMgPSAic3FydCIsIGFscGhhID0gLjQpICsKICAgICAgI2dlb21fcG9pbnQoZGF0YSA9IHBtLCBhZXMoeCA9IGxvbiwgeSA9IGxhdCksIHNpemUgPSAxLjUsIGFscGhhID0yLAogICAgICAjICBzaGFwZSA9IDIzLCBmaWxsID0gImRhcmtyZWQiKSArCiAgICAgICAgY29vcmRfc2YoeGxpbSA9IGMoLTEyNSwgLTY2KSwgeWxpbSA9IGMoMjQuNSwgNTApLCBleHBhbmQgPSBGQUxTRSkrCiAgZ2d0aXRsZSgiUHJlZGljdGVkIFBNMi41IGxldmVscyIpCgpwcmVkCgpgYGAKCgpgYGB7cn0KbGlicmFyeShjb3dwbG90KQoKY293cGxvdDo6cGxvdF9ncmlkKG1vbml0b3JzLCB0cnV0aCwgcHJlZCkKCgpgYGAKCgoKIyMgU3VtbWFyeQoKTGV0J3MgcmV2aWV3IGV2ZXJ5dGhpbmc6CgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI4MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsImVjb3N5c3RlbS5wbmciKSkKYGBgCgoKV2UgaGF2ZSBleHBsb3JlZCBncmF2YW1ldHJpYyBmaW5lIHBhcnRpY3VsYXRlIG1hdHRlciBhaXIgcG9sbHV0aW9uIG1vbml0b3JpbmcgZGF0YS4gV2UgaGF2ZSB1dGlsaXplZCB0aGUgdGlkeW1vZGVscyBlY29zeXN0ZW0gb2YgcGFja2FnZXMgdG8gcHJlZGljdCBtb25pdG9yIHZhbHVlcyB1c2luZyBhIHZhcmlldHkgb2YgcHJlZGljdG9ycywgYWxzbyBrbm93biBhcyBleHBsYW5hdG9yeSB2YXJhaWJsZXMsIGluY2x1ZGluZyBzYXR0ZWxpdGUgZGF0YSwgcm9hZCBkZW5zaXR5IGRhdGEsIGFuZCBwb3B1bGF0aW9uIGRlbnNpdHksIGFtb25nIG90aGVycy4gT3VyIG1vZGVsIGNvdWxkIGJlIGV4dGVuZGVkIHRvIGJlIHVzZWQgdG8gcHJlZGljdCBwb2xsdXRpb24gbGV2ZWxzIGluIGFyZWFzIHdpdGhvdXQgbW9uaXRvcmluZywgdG8gaGVscCBpZGVudGlmeSByZWdpb25zIHdoZXJlIHBvcHVsYXRpb25zIG1heWJlIGVzcGVjaWFsbHkgYXQgcmlzayBmb3IgdGhlIGhlYWx0aCBlZmZlY3RzIG9mIGFpciBwb2xsdXRpb24uICAKCldlIGxlYXJuZWQgdGhhdCB0aGVyZSBhcmUgdHdvIG1ham9yIHR5cGVzIG9mIHdoYXQgaXMgY2FsbGVkIHN1cGVydmlzZWQgbWFjaGluZSBsZWFybmluZzogcHJlZGljdGlvbiBhbmQgY2xhc3NpZmljYXRpb24uIFdlIGxlYXJuZWQgdGhhdCBwcmVkaWN0aW9uIGlzIHVzZWQgd2hlbiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBpcyBudW1lcmljIGFuZCBjbGFzc2lmYWN0aW9uIGlzIHBlcmZvcm1lZCB3aGVuIHRoZSBvdXRjb21lIHZhcmlhYmxlIGlzIGNhdGVnb3JpY2FsLiAgCgpXZSBwZXJmb3JtZWQgdGhlIG1ham9yIHN0ZXBzIG9mIG1hY2hpbmUgbGVhcm5pbmcgdGhhdCB3ZSBpbnRyb2R1Y2VkIGluIHRoZSBiZWdpbm5pbmcgb2YgdGhlIGRhdGEgYW5hbHlzaXM6ICAKCjEpIERhdGEgZXhwbG9yYXRpb24gIAoKV2UgdXNlZCBhIHBhY2thZ2VzIGxpa2UgYHNraW1yYCwgYHN1bW1hcnl0b29sc2AsIGBjb3JycGxvdGAsIGBnZ2NvcnJwbG90YCwgYW5kIGBHR2FsbHlgIHRvIGJldHRlciB1bmRlcnN0YW5kIG91ciBkYXRhLiBUaGVzZSBwYWNrYWdlcyBnYXZlIGNhbiB0ZWxsIHVzIGhvdyBtYW55IG1pc3NpbmcgdmFsdWVzIGVhY2ggdmFyaWFibGUgaGFzIChpZiBhbnkpLCB0aGUgY2xhc3Mgb2YgZWFjaCB2YXJpYWJsZSwgdGhlIGRpc3RyaWJ1dGlvbiBvZiB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUsIHRoZSBzcGFyc2l0eSBvZiBlYWNoIHZhcmlhYmxlLCBhbmQgdGhlIGxldmVsIG9mIGNvcnJlbGF0aW9uIGJldHdlZW4gdmFyaWFibGVzLiAgCgoyKSBEYXRhIHNwbGl0dGluZyAKCldlIHVzZWQgdGhlIGByc2FtcGxlYCBwYWNrYWdlIHRvIGZpcnN0IHBlcmZvcm0gYW4gaW5pdGlhbCBzcGxpdCBvZiBvdXIgZGF0YSBpbnRvIHR3byBwZWljZXM6IGEgdHJhaW5pbmcgc2V0IGFuZCBhIHRlc3Rpbmcgc2V0LiBUaGUgdHJhaW5pbmcgc2V0IHdhcyB1c2VkIHRvIG9wdGltaXplIHRoZSBtb2RlbCwgd2hpbGUgdGhlIHRlc3Rpbmcgc2V0IHdhcyB1c2VkIG9ubHkgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIG91ciBmaW5hbCBtb2RlbC4gV2UgYWxzbyB1c2VkIHRoZSBgcnNhbXBsZWAgcGFja2FnZSB0byBjcmVhdGUgY3Jvc3MgdmFsaWRhdGlvbiBzdWJzZXRzIG9mIG91ciB0cmFpbmluZyBkYXRhLiBUaGlzIGFsbG93ZWQgdXMgdG8gYmV0dGVyIGFzc2VzcyB0aGUgcGVyZm9ybWFuY2Ugb2Ygb3VyIHRlc3RlZCBtb2RlbHMgdXNpbmcgb3VyIHRyYWluaW5nIGRhdGEuICAKCjMpIFZhcmlhYmxlIGFzc2lnbm1lbnQgYW5kIHByZS1wcm9jZXNzaW5nICAgCgpXZSB1c2VkIHRoZSBgcmVjaXBlc2AgcGFja2FnZSB0byBhc3NpZ24gdmFyaWFibGUgcm9sZXMgKHN1Y2ggYXMgb3V0Y29tZSwgcHJlZGljdG9yLCBhbmQgaWQgdmFyaWFibGUpLiBXZSBhbHNvIHVzZWQgdGhpcyBwYWNrYWdlIHRvIGNyZWF0ZSBhIHJlY2lwZSBmb3IgcHJlLXByb2Nlc3Npbmcgb3VyIHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEuIFRoaXMgaW52b2x2ZWQgc3RlcHMgc3VjaCBhczogYCBzdGVwX2R1bW15YCB0byBjcmVhdGUgZHVtbXkgbnVtZXJpYyBlbmNvZGluZ3Mgb2Ygb3VyIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgYHN0ZXBfY29ycmAgdG8gcmVtb3ZlIGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlcywgYHN0ZXBfbnp2YCB0byByZW1vdmUgbmVhciB6ZXJvIHZhcmlhbmNlIHZhcmlhYmVscyB0aGF0IHdvdWxkIGNvbnRpYnV0ZSBsaXR0bGUgdG8gb3VyIG1vZGVsIGFuZCBwb3RlbnRpYWxseSBhZGQgbm9pc2UuICBXZSBsZWFybmVkIHRoYXQgb25jZSBvdXIgcmVjaXBlIHdhcyBjcmVhdGVkIGFuZCBwcmVwcGVkIHVzaW5nIGBwcmVwKClgd2UgY291bGQgZXh0cmFjdCB0aGUgcHJlLXByb2Nlc3NlZCB0cmFpbmluZyBkYXRhIHVzaW5nIGBqdWljZSgpYCBvciBvdXIgcHJlLXByb2Nlc3NlZCB0ZXN0aW5nIGRhdGEgdXNpbmcgYGJha2UoKWAuIFdlIGFsc28gbGVhcm5lZCB0aGF0IGlmIHdlIHVzZWQgdGhlIG5ld2VyIHdvcmtmbG93cyBwYWNrYWdlIHRoYXQgd2UgZGlkIG5vdCBuZWVkIHRvIHRoZSBgcHJlcCgpYCwgYGp1aWNlKClgLCBvciBgYmFrZSgpYCBmdW5jdGlvbnMsIGJ1dCB0aGF0IGl0IGlzIHN0aWxsIHVzZWZ1bCB0byBrbm93IGhvdyB0byBkbyBzbyBpZiB3ZSB3YW50IHRvIGxvb2sgYXQgb3VyIGRhdGEgYW5kIGhvdyB0aGUgcmVjaXBlIGlzIGluZmx1ZW5jaW5nIGl0IG1vcmUgZGVlcGx5LiAgCgo0KSBNb2RlbCBzcGVjaWZpY2F0aW9uLCBmaXR0aW5nLCB0dW5pbmcgYW5kIHBlcmZvcm1hbmNlIGV2YWx1YXRpb24gdXNpbmcgdGhlIHRyYWluaW5nIGRhdGEgIAoKV2UgbGVhcm5lZCB0aGF0IHRoZSBtb2RlbCBuZWVkcyB0byBmaXJzdCBiZSBmaXQgdG8gdGhlIHRyYWluaW5nIGRhdGEuIFdlIGxlYXJuZWQgdGhhdCBpbiBib3RoIGNsYXNzaWZpY2F0aW9uIGFuZCBwcmVkaWN0aW9uLCB0aGUgbW9kZWwgaXMgZml0IHRvIHRoZSB0cmFpbmcgZGF0YSBhbmQgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBhcmUgdXNlZCB0byBlc3RpbWF0ZSBudW1lcmljIHZhbHVlcyAoaW4gdGhlIGNhc2Ugb2YgcHJlZGljdGlvbikgb3IgY2F0ZWdvcmljYWwgdmFsdWVzIChpbiB0aGUgY2FzZSBvZiBjbGFzc2lmaWNhdGlvbikgb2YgdGhlIG91dGNvbWUgdmFyaWFibGUgb2YgaW50ZXJlc3QuIFdlIGxlYXJuZWQgdGhhdCB3ZSBzcGVjaWZ5IHRoZSBtb2RlbCBhbmQgaXRzIHNwZWNpZmljYXRpb25zIHVzaW5nIHRoZSBgcGFybnNpcGAgcGFja2FnZSBhbmQgdGhhdCB3ZSBhbHNvIHVzZSB0aGlzIHBhY2thZSB0byBmaXQgdGhlIG1vZGVsIHVzaW5nIHRoZSBgZml0KClgIGZ1bmN0aW9uLiBXZSBsZWFybmVkIHRoYXQgd2UgaWYganVzdCB1c2UgYHBhcnNuaXBgIHRvIGZpdCB0aGUgbW9kZWwsIHRoZW4gd2UgbmVlZCB0byB1c2UgdGhlIHByZS1wcm9jZXNzZWQgdHJhaW5pbmcgZGF0YSAob3V0cHV0IGZyb20gYGp1aWNlKClgKS4gV2UgbGVhcm5lZCB0aGF0IHdlIGNhbiB1c2UgdGhlIHJhdyB0cmFpbmluZyBkYXRhIGlmIHdlIHVzZSB0aGUgYHdvcmtmbG93c2AgcGFja2FnZSB0byBjcmVhdGUgYSB3b3JrZmxvdyB0aGF0IHByZS1wcm9jZXNzZXMgb3VyIGRhdGEgZm9yIHVzLiAgIAoKV2UgbGVhcm5lZCB0aGF0IGlmIHRoZSBtb2RlbCBmaXRzIHdlbGwgdGhhbiB0aGUgZXN0aW1hdGVkIHZhbHVlcyB3aWxsIGJlIHZlcnkgc2ltaWxhciB0byB0aGUgdHJ1ZSBvdXRjb21lIHZhcmlhYmxlIHZhbHVlcyBpbiBvdXIgdHJhaW5pbmcgZGF0YS4gV2UgbGVhcm5lZCB0aGF0IHdlIGNhbiBhc3Nlc3MgbW9kZWwgcGVmb3JtYW5jZSB1c2luZyB0aGUgYHlhcmRzdGlja2AgcGFja2FnZSBhbmQgdGhlIGBtZXRyaWNzKClgIGZ1bmN0aW9uLiBXZSBhbHNvIGxlYXJuZWQgdGhhdCB3ZSBjYW4gdXNlIHN1YnNldHMgb2Ygb3VyIHRyYWluaW5nIGRhdGEgKHdoaWNoIHdlIGNyZWF0ZWQgd2l0aCB0aGUgYHJzYW1wbGVgIHBhY2thZ2UpIHRvIHBlcmZvcm0gY3Jvc3MgdmFsaWRhdGlvbiB0byBnZXQgYSBiZXR0ZXIgZXN0aW1hdGUgYWJvdXQgdGhlIHBlcmZvcm1hbmNlIG9mIG91ciBtb2RlbCB1c2luZyBvdXIgdHJhaW5pbmcgZGF0YSwgYXMgd2Ugd2FudCBvdXIgcmVzdWx0cyB0byBiZSBnZW5lcmFsaXphYmxlIGFuZCB0byBwZXJmb3JtIHdlbGwgd2l0aCBvdGhlciBkYXRhLCBub3QganVzdCBvdXIgdHJhaW5pbmcgZGF0YS4gV2UgdXNlZCB0aGUgYGZpdF9yZXNhbXBsZXMoKWAgZnVuY3Rpb24gb2YgdGhlIHR1bmUgcGFja2FnZSB0byBmaXQgb3VyIG1vZGVsIG9uIG91ciBkaWZmZXJlbnQgdHJhaW5pbmcgZGF0YSBzdWJzZXRzIGFuZCB0aGUgYGNvbGxlY3RfbWV0cmljcygpYCBmdW5jdGlvbiAoYWxzbyBvZiB0aGUgYHR1bmVgIHBhY2thZ2UpIHRvIGV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmNlIHVzaW5nIHRoZXNlIHN1YnNldHMuICBXZSBhbHNvIGxlYXJuZWQgdGhhdCB3ZSBjYW4gcG90ZW50aWFsbHkgaW1wcm92ZSBtb2RlbCBwZXJmb3JtYW5jZSBieSB0dW5pbmcgYXNwZWN0cyBhYm91dCB0aGUgbW9kZWwgY2FsbGVkIFtoeXBlcnBhcmFtZXRlcnNdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb21tdW5pdHkvdHV0b3JpYWxzL3BhcmFtZXRlci1vcHRpbWl6YXRpb24tbWFjaGluZS1sZWFybmluZy1tb2RlbHMpe3RhcmdldD0iX2JsYW5rIn0gdG8gZGV0ZXJtaW5lIHRoZSBiZXN0IG9wdGlvbiBmb3IgbW9kZWwgcGVyZm9ybWFuY2UuIFdlIGxlYXJuZWQgdGhhdCB3ZSBjYW4gZG8gdGhpcyB1c2luZyB0aGUgYHR1bmVgIGFuZCBgZGlhbHNgIHBhY2thZ2VzIGFuZCBldmFsdWF0aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBvdXIgbW9kZWwgd2l0aCB0aGUgZGlmZmVyZW50IGh5cGVycGFyYW1ldGVyIG9wdGlvbnMgYW5kIG91ciB0cmFpbmluZyBkYXRhIHN1YnNldHMgdGhhdCB3ZSB1c2VkIGZvciBjcm9zcyB2YWxpZGF0aW9uLiBBZnRlciB3ZSB0ZXN0ZWQgc2V2ZXJhbCBkaWZmZXJlbnQgbWV0aG9kcyB0byBtb2RlbCBvdXIgZGF0YSwgd2UgY29tcGFyZWQgdGhlbSB0byBjaG9vc2UgdGhlIGJlc3QgcGVyZm9ybWluZyBtb2RlbCBhcyBvdXIgZmluYWwgbW9kZWwuICAKCjUpIE92ZXJhbGwgbW9kZWwgcGVyZm9ybWFuY2UgZXZhbHVhdGlvbiAgCgpPbmNlIHdlIGNob3NlIG91ciBmaW5hbCBtb2RlbCwgd2UgZXZhbHVhdGVkIHRoZSBmaW5hbCBtb2RlbCBwZXJmb3JtYW5jZSB1c2luZyB0aGUgdGVzdGluZyBkYXRhLiBUaGlzIGdpdmVzIHVzIGEgYmV0dGVyIGVzdGltYXRlIGFib3V0IGhvdyB3ZWxsIHRoZSBtb2RlbCB3aWxsIHByZWRpY3Qgb3IgY2xhc3NpZnkgdGhlIG91dGNvbWUgdmFyaWFibGUgb2YgaW50ZXJlc3Qgd2l0aCBuZXcgaW5kZXBlbmRlbnQgZGF0YS4gSWRlYWxseSBvbmUgd291bGQgYWxzbyBwZXJmb3JtIGFuIGV2YWx1YXRpb24gd2l0aCBpbmRlcGVuZGVudCBkYXRhIHRvIHByb3ZpZGUgYSBzZW5zZSBvZiBob3cgZ2VuZXJhbGl6YWJsZSB0aGUgbW9kZWwgaXMgdG8gb3RoZXIgZGF0YSBzb3VyY2VzLiAKCldlIGZpcnN0IGZpdCBvdXIgbW9kZWwgdG8gb3VyIHRlc3RpbmcgZGF0YSB1c2luZyBlaXRoZXIganVzdCBwYXJzbmlwIGFuZCB0aGUgcHJlLXByb2Nlc3NlZCB0ZXN0aW5nIGRhdGEgKHVzaW5nIHRoZSBgYmFrZSgpYCByZWNpcGVzIGZ1bmN0aW9uKSwgb3Igb3VyIHJhdyB0ZXN0aW5nIGRhdGEgaWYgd2UgdXNlZCBhIHdvcmtmbG93LiBXZSB1c2VkIHRoZSBzYW1lIHBlcmZvcm1hbmNlIGV2YWx1YXRpb24gZnVuY3Rpb25zIChgeWFyZHN0aWNrOjptZXRyaWNzKClgICBhbmQgYHR1bmU6OmNvbGxlY3RfbWV0cmljcygpYCh3aGVuIHVzaW5nIGNyb3NzIHZhbGlkaW9uKSkuIFdlIGFsc28gbGVhcm5lZCBob3cgd2UgY2FuIHVzZSB0aGUgYGxhc3RfZml0KClgIGZ1bmN0aW9uIG9mIHRoZSBgdHVuZWAgcGFja2FnZSBpZiB3ZSB1c2VkIGEgd29ya2Zsb3cgdG8gZ2V0IHRoZSB0ZXN0IGRhdGEgcGVyZm9ybWFuY2UgdXNpbmcgdGhlIGluaXRpYWwgZGF0YSBhbmQgdGhlIHRlc3RpbmcvdHJhaW5pbmcgc3BsaXQgaW5mb3JtYXRpb24uCgpBbmFseXNlcyBsaWtlIHRoZSBvbmUgaW4gb3VyIGNhc2Ugc3R1ZHkgYXJlIGltcG9ydGFudCBmb3IgZGVmaW5pbmcgd2hpY2ggZ3JvdXBzIGNvdWxkIGJlbmVmaXQgdGhlIG1vc3QgZnJvbSBpbnRlcnZlbnRpb25zLCBlZHVjYXRpb24sIGFuZCBwb2xpY3kgY2hhbmdlcyB3aGVuIGF0dGVtcHRpbmcgdG8gbWl0aWdhdGUgcHVibGljIGhlYWx0aCBjaGFsbGVuZ2VzLiBZb3UgY2FuIHNlZSBpbiB0aGlzIFthcnRpY2xlXShodHRwczovL3d3dy5uZWptLm9yZy9kb2kvZnVsbC8xMC4xMDU2L05FSk1vYTE3MDI3NDcpe3RhcmdldD0iX2JsYW5rIn0gdGhhdCBtYW55IGFkZGl0aW9uYWwgY29uc2lkZXJhdGlvbnMgd291bGQgYmUgaW52b2x2ZWQgdG8gYWRlcXVhdGVseSB1bmRlcnN0YW5kIHRoZSBkYXRhIGVub3VnaCB0byByZWNvbW1lbmQgcG9saWN5IGNoYW5nZXMuCgojIyMgU3VnZ2VzdGVkIEhvbWV3b3JrCgpTdHVkZW50cyBjYW4gcHJlZGljdCBhaXIgcG9sbHV0aW9uIG1vbml0b3IgdmFsdWVzIHVzaW5nIGEgZGlmZmVyZW50IGFsZ29yaXRobSBhbmQgcHJvdmlkZSBhbiBleHBsYW5hdGlvbiBmb3IgaG93IHRoYXQgYWxnb3JpdGhtIHdvcmtzIGFuZCB3aHkgaXQgbWF5IGJlIGEgZ29vZCBjaG9pY2UgZm9yIG1vZGVsaW5nIHRoaXMgZGF0YS4KCiMjIyBIZWxwZnVsIExpbmtzCgoxKSBBIHJldmlldyBvZiBbdGlkeW1vZGVsc10oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxOS8wNi8xOS9hLWdlbnRsZS1pbnRyby10by10aWR5bW9kZWxzLyl7dGFyZ2V0PSJfYmxhbmsifSAgCjIpIEEgW2NvdXJzZSBvbiB0aWR5bW9kZWxzXShodHRwczovL2p1bGlhc2lsZ2UuY29tL2Jsb2cvdGlkeW1vZGVscy1tbC1jb3Vyc2UvKXt0YXJnZXQ9Il9ibGFuayJ9IGJ5IEp1bGlhIFNpbGdlICAKMykgW01vcmUgZXhhbXBsZXMsIGV4cGxhbmF0aW9ucywgYW5kIGluZm8gYWJvdXQgdGlkeW1vZGVscyBkZXZlbG9wbWVudF0oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvbGVhcm4vKXt0YXJnZXQ9Il9ibGFuayJ9IGZyb20gdGhlIGRldmVsb3BlcnMgIAo0KSBBIGd1aWRlIGZvciBbcHJlLXByb2Nlc3Npbmcgd2l0aCByZWNpcGVzXShodHRwOi8vd3d3LnJlYmVjY2FiYXJ0ZXIuY29tL2Jsb2cvMjAxOS0wNi0wNl9wcmVfcHJvY2Vzc2luZy8pe3RhcmdldD0iX2JsYW5rIn0gIAo1KSBBIFtndWlkZV0oaHR0cHM6Ly9icmlhdHRlLmdpdGh1Yi5pby9nZ2NvcnIvKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciB1c2luZyBHR2FsbHkgdG8gY3JlYXRlIGNvcnJlbGF0aW9uIHBsb3RzICAKNikgQSBbZ3VpZGVdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvYmxvZy8yMDE4LzExL3BhcnNuaXAtMC0wLTEvKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciB1c2luZyBwYXJzbmlwIHRvIHRyeSBkaWZmZXJlbnQgYWxnb3JpdGhtcyBvciBlbmdpbmVzICAKNykgQSBbbGlzdCBvZiByZWNpcGUgZnVuY3Rpb25zXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3JlY2lwZXMvcmVmZXJlbmNlL2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gIAo4KSBBIGdyZWF0IGJsb2cgcG9zdCBhYm91dCBbY3Jvc3MgdmFsaWRhdGlvbl0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3RyYWluLXRlc3Qtc3BsaXQtYW5kLWNyb3NzLXZhbGlkYXRpb24taW4tcHl0aG9uLTgwYjYxYmVjYTRiNil7dGFyZ2V0PSJfYmxhbmsifSAgCjkpIEEgZGlzY3Vzc2lvbiBhYm91dCBbZXZhbHVhdGluZyBtb2RlbCBwZXJmb3JtYW5jZV0oaHR0cHM6Ly9tZWRpdW0uY29tL0BsaW1hdmFsbGFudGluL21ldHJpY3MtdG8tbWVhc3VyZS1tYWNoaW5lLWxlYXJuaW5nLW1vZGVsLXBlcmZvcm1hbmNlLWU4Yzk2MzY2NTQ3Nil7dGFyZ2V0PSJfYmxhbmsifSBmb3IgYSBkZWVwZXIgZXhwbGFuYXRpb24gYWJvdXQgaG93IHRvIGV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmNlICAKMTApIFtSU3R1ZGlvIGNoZWF0c2hlZXRzXShodHRwczovL3JzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pe3RhcmdldD0iX2JsYW5rIn0KMTEpIEFuIFtleHBsYW5hdGlvbl0oaHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL3N1cGVydmlzZWQtdnMtdW5zdXBlcnZpc2VkLWxlYXJuaW5nLTE0ZjY4ZTMyZWE4ZCl7dGFyZ2V0PSJfYmxhbmsifSBvZiBzdXBlcnZpc2VkIHZzIHVuc3VwZXJ2aXNlZCBtYWNoaW5lIGxlYXJuaW5nIGFuZCBiaWFzLXZhcmlhbmNlIHRyYWRlb2ZmLgoxMikgQSB0aG9yb3VnaCBbZXhwbGFuYXRpb25dKGh0dHBzOi8vcm95YWxzb2NpZXR5cHVibGlzaGluZy5vcmcvZG9pLzEwLjEwOTgvcnN0YS4yMDE1LjAyMDIjOn46dGV4dD1QcmluY2lwYWwlMjBjb21wb25lbnQlMjBhbmFseXNpcyUyMChQQ0EpJTIwaXMsdmFyaWFibGVzJTIwdGhhdCUyMHN1Y2Nlc3NpdmVseSUyMG1heGltaXplJTIwdmFyaWFuY2UuKXt0YXJnZXQ9Il9ibGFuayJ9IG9mIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMuCjEzKSBJZiB5b3UgaGF2ZSBhY2Nlc3MsIHRoaXMgaXMgYSBncmVhdCBbZGlzY3Vzc2lvbl0oaHR0cHM6Ly93d3cudGFuZGZvbmxpbmUuY29tL2RvaS9hYnMvMTAuMTA4MC8wMDAzMTMwNS4xOTg0LjEwNDgzMTgzKXt0YXJnZXQ9Il9ibGFuayJ9ICBhYm91dCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGluZGVwZW5kZW5jZSwgb3J0aG9nb25hbGl0eSwgYW5kIGxhY2sgb2YgY29ycmVsYXRpb24uCjE0KSBHcmVhdCBbdmlkZW8gZXhwbGFuYXRpb25dKGh0dHBzOi8veW91dHUuYmUvX1VWSG5lQlVCVzApe3RhcmdldD0iX2JsYW5rIn0gb2YgUENBLiAgCgo8dT5UZXJtcyBhbmQgY29uY2VwdHMgY292ZXJlZDo8L3U+ICAKW1RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gIApbcGFyYW1ldGVycyBhbmQgaHlwZXJwYXJhbWV0ZXJzXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L3R1dG9yaWFscy9wYXJhbWV0ZXItb3B0aW1pemF0aW9uLW1hY2hpbmUtbGVhcm5pbmctbW9kZWxzKXt0YXJnZXQ9Il9ibGFuayJ9ICAKCjx1PlBhY2thZ2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5OiA8L3U+CgogUGFja2FnZSAgIHwgVXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tCgoK